From c75b029160248b6935e5c0f5156cc7a870311e82 Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Sun, 26 Jun 2022 18:02:27 -0300 Subject: real pragma, and some eslint fixes --- .prettierrc.json | 2 +- package.json | 3 +- src/data/cacheable-object.js | 33 +- src/data/patches.js | 45 +- src/data/serialize.js | 6 +- src/data/things.js | 691 +++++++++++++++--------------- src/data/validators.js | 42 +- src/data/yaml.js | 595 +++++++++++++------------- src/file-size-preloader.js | 8 +- src/gen-thumbs.js | 113 +++-- src/listing-spec.js | 500 +++++++++++----------- src/misc-templates.js | 335 +++++++-------- src/page/album-commentary.js | 87 ++-- src/page/album.js | 223 +++++----- src/page/artist-alias.js | 16 +- src/page/artist.js | 258 +++++------ src/page/flash.js | 160 ++++--- src/page/group.js | 128 +++--- src/page/homepage.js | 70 +-- src/page/index.js | 28 +- src/page/listing.js | 104 ++--- src/page/news.js | 60 +-- src/page/static.js | 18 +- src/page/tag.js | 46 +- src/page/track.js | 157 ++++--- src/repl.js | 42 +- src/static/client.js | 208 ++++----- src/static/lazy-loading.js | 18 +- src/upd8.js | 992 +++++++++++++++++++++---------------------- src/url-spec.js | 90 ++-- src/util/cli.js | 70 +-- src/util/colors.js | 4 +- src/util/find.js | 62 +-- src/util/html.js | 60 +-- src/util/io.js | 10 +- src/util/link.js | 94 ++-- src/util/magic-constants.js | 8 +- src/util/node-utils.js | 10 +- src/util/replacer.js | 84 ++-- src/util/serialize.js | 20 +- src/util/sugar.js | 110 ++--- src/util/urls.js | 36 +- src/util/wiki-data.js | 84 ++-- 43 files changed, 2848 insertions(+), 2882 deletions(-) diff --git a/.prettierrc.json b/.prettierrc.json index 7d30a07..bba89fe 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -1,6 +1,6 @@ { "arrowParens": "always", - "bracketSpacing": true, + "bracketSpacing": false, "printWidth": 80, "proseWrap": "never", "quoteProps": "as-needed", diff --git a/package.json b/package.json index 938ec6a..1ca350e 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,8 @@ "hsmusic": "./src/upd8.js" }, "scripts": { - "test": "tape test/**/*.js" + "test": "tape test/**/*.js", + "dev": "prettier --loglevel warn --write . && eslint src && node src/upd8.js" }, "dependencies": { "fix-whitespace": "^1.0.4", diff --git a/src/data/cacheable-object.js b/src/data/cacheable-object.js index 4728193..fe1817f 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 937fb09..dc757fa 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 13b20e1..a4206fd 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 c86add3..e2bbfd7 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>/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('
')[0], + dependencies: ['description'], + compute: ({description}) => description.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('
')[0], + compute: ({content}) => content.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 eab2689..8d92239 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 5058bb3..a425576 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(":
")) { + const lines = String(text).split('\n'); + if (!lines[0].replace(/<\/b>/g, '').includes(':')) { 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("")) { + if (contributors.length === 1 && contributors[0].startsWith('')) { 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, }); diff --git a/src/file-size-preloader.js b/src/file-size-preloader.js index e34cee5..363fb4c 100644 --- a/src/file-size-preloader.js +++ b/src/file-size-preloader.js @@ -1,5 +1,5 @@ -// @format -// +/** @format */ + // Very simple, bare-bones file size loader which takes a bunch of file // paths, gets their filesizes, and resolves a promise when it's done. // @@ -19,8 +19,8 @@ // This only processes files one at a time because I'm lazy and stat calls // are very, very fast. -import { stat } from "fs/promises"; -import { logWarn } from "./util/cli.js"; +import {stat} from 'fs/promises'; +import {logWarn} from './util/cli.js'; export default class FileSizePreloader { #paths = []; diff --git a/src/gen-thumbs.js b/src/gen-thumbs.js index cd7196d..82ddb34 100644 --- a/src/gen-thumbs.js +++ b/src/gen-thumbs.js @@ -1,7 +1,6 @@ #!/usr/bin/env node +/** @format */ -// @format -// // Ok, so the d8te is 3 March 2021, and the music wiki was initially released // on 15 November 2019. That is 474 days or 11376 hours. In my opinion, and // pro8a8ly the opinions of at least one other person, that is WAY TOO LONG @@ -74,18 +73,18 @@ // unused). This is just to make the code more porta8le and sta8le, long-term, // since it avoids a lot of otherwise implic8ted maintenance. -"use strict"; +'use strict'; -const CACHE_FILE = "thumbnail-cache.json"; +const CACHE_FILE = 'thumbnail-cache.json'; const WARNING_DELAY_TIME = 10000; -import { spawn } from "child_process"; -import { createHash } from "crypto"; -import * as path from "path"; +import {spawn} from 'child_process'; +import {createHash} from 'crypto'; +import * as path from 'path'; -import { readdir, readFile, writeFile } from "fs/promises"; // Whatcha know! Nice. +import {readdir, readFile, writeFile} from 'fs/promises'; // Whatcha know! Nice. -import { createReadStream } from "fs"; // Still gotta import from 8oth tho, for createReadStream. +import {createReadStream} from 'fs'; // Still gotta import from 8oth tho, for createReadStream. import { logError, @@ -93,15 +92,15 @@ import { logWarn, parseOptions, progressPromiseAll, -} from "./util/cli.js"; +} from './util/cli.js'; -import { commandExists, isMain, promisifyProcess } from "./util/node-utils.js"; +import {commandExists, isMain, promisifyProcess} from './util/node-utils.js'; -import { delay, queue } from "./util/sugar.js"; +import {delay, queue} from './util/sugar.js'; function traverse( startDirPath, - { filterFile = () => true, filterDir = () => true } = {} + {filterFile = () => true, filterDir = () => true} = {} ) { const recursive = (names, subDirPath) => Promise.all( @@ -116,24 +115,24 @@ function traverse( ) ).then((pathArrays) => pathArrays.flatMap((x) => x)); - return readdir(startDirPath).then((names) => recursive(names, "")); + return readdir(startDirPath).then((names) => recursive(names, '')); } function readFileMD5(filePath) { return new Promise((resolve, reject) => { - const md5 = createHash("md5"); + const md5 = createHash('md5'); const stream = createReadStream(filePath); - stream.on("data", (data) => md5.update(data)); - stream.on("end", (data) => resolve(md5.digest("hex"))); - stream.on("error", (err) => reject(err)); + stream.on('data', (data) => md5.update(data)); + stream.on('end', (data) => resolve(md5.digest('hex'))); + stream.on('error', (err) => reject(err)); }); } async function getImageMagickVersion(spawnConvert) { - const proc = spawnConvert(["--version"], false); + const proc = spawnConvert(['--version'], false); - let allData = ""; - proc.stdout.on("data", (data) => { + let allData = ''; + proc.stdout.on('data', (data) => { allData += data.toString(); }); @@ -145,7 +144,7 @@ async function getImageMagickVersion(spawnConvert) { const match = allData.match(/Version: (.*)/i); if (!match) { - return "unknown version"; + return 'unknown version'; } return match[1]; @@ -153,13 +152,13 @@ async function getImageMagickVersion(spawnConvert) { async function getSpawnConvert() { let fn, description, version; - if (await commandExists("convert")) { - fn = (args) => spawn("convert", args); - description = "convert"; - } else if (await commandExists("magick")) { + if (await commandExists('convert')) { + fn = (args) => spawn('convert', args); + description = 'convert'; + } else if (await commandExists('magick')) { fn = (args, prefix = true) => - spawn("magick", prefix ? ["convert", ...args] : args); - description = "magick convert"; + spawn('magick', prefix ? ['convert', ...args] : args); + description = 'magick convert'; } else { return [`no convert or magick binary`, null]; } @@ -173,28 +172,28 @@ async function getSpawnConvert() { return [`${description} (${version})`, fn]; } -function generateImageThumbnails(filePath, { spawnConvert }) { +function generateImageThumbnails(filePath, {spawnConvert}) { const dirname = path.dirname(filePath); const extname = path.extname(filePath); const basename = path.basename(filePath, extname); - const output = (name) => path.join(dirname, basename + name + ".jpg"); + const output = (name) => path.join(dirname, basename + name + '.jpg'); - const convert = (name, { size, quality }) => + const convert = (name, {size, quality}) => spawnConvert([ filePath, - "-strip", - "-resize", + '-strip', + '-resize', `${size}x${size}>`, - "-interlace", - "Plane", - "-quality", + '-interlace', + 'Plane', + '-quality', `${quality}%`, output(name), ]); return Promise.all([ - promisifyProcess(convert(".medium", { size: 400, quality: 95 }), false), - promisifyProcess(convert(".small", { size: 250, quality: 85 }), false), + promisifyProcess(convert('.medium', {size: 400, quality: 95}), false), + promisifyProcess(convert('.small', {size: 250, quality: 85}), false), ]); return new Promise((resolve, reject) => { @@ -208,10 +207,10 @@ function generateImageThumbnails(filePath, { spawnConvert }) { export default async function genThumbs( mediaPath, - { queueSize = 0, quiet = false } = {} + {queueSize = 0, quiet = false} = {} ) { if (!mediaPath) { - throw new Error("Expected mediaPath to be passed"); + throw new Error('Expected mediaPath to be passed'); } const quietInfo = quiet ? () => null : logInfo; @@ -221,16 +220,16 @@ export default async function genThumbs( // thumbnail-cache.json is 8eing passed through, for some reason. const ext = path.extname(name); - if (ext !== ".jpg" && ext !== ".png") return false; + if (ext !== '.jpg' && ext !== '.png') return false; const rest = path.basename(name, ext); - if (rest.endsWith(".medium") || rest.endsWith(".small")) return false; + if (rest.endsWith('.medium') || rest.endsWith('.small')) return false; return true; }; const filterDir = (name) => { - if (name === ".git") return false; + if (name === '.git') return false; return true; }; @@ -242,7 +241,7 @@ export default async function genThumbs( logInfo`You can find info to help install ImageMagick on Linux, Windows, or macOS`; logInfo`from its official website: ${`https://imagemagick.org/script/download.php`}`; logInfo`If you have trouble working ImageMagick and would like some help, feel free`; - logInfo`to drop a message in the HSMusic Discord server! ${"https://hsmusic.wiki/discord/"}`; + logInfo`to drop a message in the HSMusic Discord server! ${'https://hsmusic.wiki/discord/'}`; return false; } else { logInfo`Found ImageMagick binary: ${convertInfo}`; @@ -256,7 +255,7 @@ export default async function genThumbs( quietInfo`Cache file successfully read.`; } catch (error) { cache = {}; - if (error.code === "ENOENT") { + if (error.code === 'ENOENT') { firstRun = true; } else { failedReadingCache = true; @@ -283,7 +282,7 @@ export default async function genThumbs( await delay(WARNING_DELAY_TIME); } - const imagePaths = await traverse(mediaPath, { filterFile, filterDir }); + const imagePaths = await traverse(mediaPath, {filterFile, filterDir}); const imageToMD5Entries = await progressPromiseAll( `Generating MD5s of image files`, @@ -292,7 +291,7 @@ export default async function genThumbs( (imagePath) => () => readFileMD5(path.join(mediaPath, imagePath)).then( (md5) => [imagePath, md5], - (error) => [imagePath, { error }] + (error) => [imagePath, {error}] ) ), queueSize @@ -385,24 +384,24 @@ export default async function genThumbs( if (isMain(import.meta.url)) { (async function () { const miscOptions = await parseOptions(process.argv.slice(2), { - "media-path": { - type: "value", + 'media-path': { + type: 'value', }, - "queue-size": { - type: "value", + 'queue-size': { + type: 'value', validate(size) { - if (parseInt(size) !== parseFloat(size)) return "an integer"; - if (parseInt(size) < 0) return "a counting number or zero"; + if (parseInt(size) !== parseFloat(size)) return 'an integer'; + if (parseInt(size) < 0) return 'a counting number or zero'; return true; }, }, - queue: { alias: "queue-size" }, + queue: {alias: 'queue-size'}, }); - const mediaPath = miscOptions["media-path"] || process.env.HSMUSIC_MEDIA; - const queueSize = +(miscOptions["queue-size"] ?? 0); + const mediaPath = miscOptions['media-path'] || process.env.HSMUSIC_MEDIA; + const queueSize = +(miscOptions['queue-size'] ?? 0); - await genThumbs(mediaPath, { queueSize }); + await genThumbs(mediaPath, {queueSize}); })().catch((err) => { console.error(err); }); diff --git a/src/listing-spec.js b/src/listing-spec.js index f561cef..d5341a5 100644 --- a/src/listing-spec.js +++ b/src/listing-spec.js @@ -1,6 +1,6 @@ -// @format +/** @format */ -import fixWS from "fix-whitespace"; +import fixWS from 'fix-whitespace'; import { chunkByProperties, @@ -8,55 +8,55 @@ import { getTotalDuration, sortAlphabetically, sortChronologically, -} from "./util/wiki-data.js"; +} from './util/wiki-data.js'; const listingSpec = [ { - directory: "albums/by-name", - stringsKey: "listAlbums.byName", + directory: 'albums/by-name', + stringsKey: 'listAlbums.byName', - data({ wikiData }) { + data({wikiData}) { return sortAlphabetically(wikiData.albumData.slice()); }, - row(album, { link, language }) { - return language.$("listingPage.listAlbums.byName.item", { + row(album, {link, language}) { + return language.$('listingPage.listAlbums.byName.item', { album: link.album(album), - tracks: language.countTracks(album.tracks.length, { unit: true }), + tracks: language.countTracks(album.tracks.length, {unit: true}), }); }, }, { - directory: "albums/by-tracks", - stringsKey: "listAlbums.byTracks", + directory: 'albums/by-tracks', + stringsKey: 'listAlbums.byTracks', - data({ wikiData }) { + data({wikiData}) { return wikiData.albumData .slice() .sort((a, b) => b.tracks.length - a.tracks.length); }, - row(album, { link, language }) { - return language.$("listingPage.listAlbums.byTracks.item", { + row(album, {link, language}) { + return language.$('listingPage.listAlbums.byTracks.item', { album: link.album(album), - tracks: language.countTracks(album.tracks.length, { unit: true }), + tracks: language.countTracks(album.tracks.length, {unit: true}), }); }, }, { - directory: "albums/by-duration", - stringsKey: "listAlbums.byDuration", + directory: 'albums/by-duration', + stringsKey: 'listAlbums.byDuration', - data({ wikiData }) { + data({wikiData}) { return wikiData.albumData - .map((album) => ({ album, duration: getTotalDuration(album.tracks) })) + .map((album) => ({album, duration: getTotalDuration(album.tracks)})) .sort((a, b) => b.duration - a.duration); }, - row({ album, duration }, { link, language }) { - return language.$("listingPage.listAlbums.byDuration.item", { + row({album, duration}, {link, language}) { + return language.$('listingPage.listAlbums.byDuration.item', { album: link.album(album), duration: language.formatDuration(duration), }); @@ -64,17 +64,17 @@ const listingSpec = [ }, { - directory: "albums/by-date", - stringsKey: "listAlbums.byDate", + directory: 'albums/by-date', + stringsKey: 'listAlbums.byDate', - data({ wikiData }) { + data({wikiData}) { return sortChronologically( wikiData.albumData.filter((album) => album.date) ); }, - row(album, { link, language }) { - return language.$("listingPage.listAlbums.byDate.item", { + row(album, {link, language}) { + return language.$('listingPage.listAlbums.byDate.item', { album: link.album(album), date: language.formatDate(album.date), }); @@ -82,10 +82,10 @@ const listingSpec = [ }, { - directory: "albums/by-date-added", - stringsKey: "listAlbums.byDateAdded", + directory: 'albums/by-date-added', + stringsKey: 'listAlbums.byDateAdded', - data({ wikiData }) { + data({wikiData}) { return chunkByProperties( wikiData.albumData .filter((a) => a.dateAddedToWiki) @@ -93,18 +93,18 @@ const listingSpec = [ if (a.dateAddedToWiki < b.dateAddedToWiki) return -1; if (a.dateAddedToWiki > b.dateAddedToWiki) return 1; }), - ["dateAddedToWiki"] + ['dateAddedToWiki'] ); }, - html(chunks, { link, language }) { + html(chunks, {link, language}) { return fixWS`
${chunks .map( - ({ dateAddedToWiki, chunk: albums }) => fixWS` + ({dateAddedToWiki, chunk: albums}) => fixWS`
${language.$( - "listingPage.listAlbums.byDateAdded.date", + 'listingPage.listAlbums.byDateAdded.date', { date: language.formatDate(dateAddedToWiki), } @@ -113,36 +113,36 @@ const listingSpec = [ ${albums .map((album) => language.$( - "listingPage.listAlbums.byDateAdded.album", + 'listingPage.listAlbums.byDateAdded.album', { album: link.album(album), } ) ) .map((row) => `
  • ${row}
  • `) - .join("\n")} + .join('\n')} ` ) - .join("\n")} + .join('\n')}
    `; }, }, { - directory: "artists/by-name", - stringsKey: "listArtists.byName", + directory: 'artists/by-name', + stringsKey: 'listArtists.byName', - data({ wikiData }) { + data({wikiData}) { return sortAlphabetically(wikiData.artistData.slice()).map((artist) => ({ artist, contributions: getArtistNumContributions(artist), })); }, - row({ artist, contributions }, { link, language }) { - return language.$("listingPage.listArtists.byName.item", { + row({artist, contributions}, {link, language}) { + return language.$('listingPage.listArtists.byName.item', { artist: link.artist(artist), contributions: language.countContributions(contributions, { unit: true, @@ -152,10 +152,10 @@ const listingSpec = [ }, { - directory: "artists/by-contribs", - stringsKey: "listArtists.byContribs", + directory: 'artists/by-contribs', + stringsKey: 'listArtists.byContribs', - data({ wikiData }) { + data({wikiData}) { return { toTracks: wikiData.artistData .map((artist) => ({ @@ -165,7 +165,7 @@ const listingSpec = [ (artist.tracksAsArtist?.length ?? 0), })) .sort((a, b) => b.contributions - a.contributions) - .filter(({ contributions }) => contributions), + .filter(({contributions}) => contributions), toArtAndFlashes: wikiData.artistData .map((artist) => ({ @@ -180,7 +180,7 @@ const listingSpec = [ : 0), })) .sort((a, b) => b.contributions - a.contributions) - .filter(({ contributions }) => contributions), + .filter(({contributions}) => contributions), // This is a kinda naughty hack, 8ut like, it's the only place // we'd 8e passing wikiData to html() otherwise, so like.... @@ -189,54 +189,54 @@ const listingSpec = [ }; }, - html({ toTracks, toArtAndFlashes, showAsFlashes }, { link, language }) { + html({toTracks, toArtAndFlashes, showAsFlashes}, {link, language}) { return fixWS`

    ${language.$( - "listingPage.misc.trackContributors" + 'listingPage.misc.trackContributors' )}

      ${toTracks - .map(({ artist, contributions }) => + .map(({artist, contributions}) => language.$( - "listingPage.listArtists.byContribs.item", + 'listingPage.listArtists.byContribs.item', { artist: link.artist(artist), contributions: language.countContributions( contributions, - { unit: true } + {unit: true} ), } ) ) .map((row) => `
    • ${row}
    • `) - .join("\n")} + .join('\n')}

    ${language.$( - "listingPage.misc" + + 'listingPage.misc' + (showAsFlashes - ? ".artAndFlashContributors" - : ".artContributors") + ? '.artAndFlashContributors' + : '.artContributors') )}

      ${toArtAndFlashes - .map(({ artist, contributions }) => + .map(({artist, contributions}) => language.$( - "listingPage.listArtists.byContribs.item", + 'listingPage.listArtists.byContribs.item', { artist: link.artist(artist), contributions: language.countContributions( contributions, - { unit: true } + {unit: true} ), } ) ) .map((row) => `
    • ${row}
    • `) - .join("\n")} + .join('\n')}
    @@ -245,10 +245,10 @@ const listingSpec = [ }, { - directory: "artists/by-commentary", - stringsKey: "listArtists.byCommentary", + directory: 'artists/by-commentary', + stringsKey: 'listArtists.byCommentary', - data({ wikiData }) { + data({wikiData}) { return wikiData.artistData .map((artist) => ({ artist, @@ -256,23 +256,23 @@ const listingSpec = [ (artist.tracksAsCommentator?.length ?? 0) + (artist.albumsAsCommentator?.length ?? 0), })) - .filter(({ entries }) => entries) + .filter(({entries}) => entries) .sort((a, b) => b.entries - a.entries); }, - row({ artist, entries }, { link, language }) { - return language.$("listingPage.listArtists.byCommentary.item", { + row({artist, entries}, {link, language}) { + return language.$('listingPage.listArtists.byCommentary.item', { artist: link.artist(artist), - entries: language.countCommentaryEntries(entries, { unit: true }), + entries: language.countCommentaryEntries(entries, {unit: true}), }); }, }, { - directory: "artists/by-duration", - stringsKey: "listArtists.byDuration", + directory: 'artists/by-duration', + stringsKey: 'listArtists.byDuration', - data({ wikiData }) { + data({wikiData}) { return wikiData.artistData .map((artist) => ({ artist, @@ -281,12 +281,12 @@ const listingSpec = [ ...(artist.tracksAsContributor ?? []), ]), })) - .filter(({ duration }) => duration > 0) + .filter(({duration}) => duration > 0) .sort((a, b) => b.duration - a.duration); }, - row({ artist, duration }, { link, language }) { - return language.$("listingPage.listArtists.byDuration.item", { + row({artist, duration}, {link, language}) { + return language.$('listingPage.listArtists.byDuration.item', { artist: link.artist(artist), duration: language.formatDuration(duration), }); @@ -294,10 +294,10 @@ const listingSpec = [ }, { - directory: "artists/by-latest", - stringsKey: "listArtists.byLatest", + directory: 'artists/by-latest', + stringsKey: 'listArtists.byLatest', - data({ wikiData }) { + data({wikiData}) { const reversedTracks = sortChronologically( wikiData.trackData.filter((t) => t.date) ).reverse(); @@ -318,10 +318,10 @@ const listingSpec = [ [ ...(track.artistContribs ?? []), ...(track.contributorContribs ?? []), - ].some(({ who }) => who === artist) + ].some(({who}) => who === artist) )?.date, })) - .filter(({ date }) => date) + .filter(({date}) => date) ).reverse(), toArtAndFlashes: sortChronologically( @@ -331,7 +331,7 @@ const listingSpec = [ [ ...(thing.coverArtistContribs ?? []), ...((!thing.album && thing.contributorContribs) || []), - ].some(({ who }) => who === artist) + ].some(({who}) => who === artist) ); return ( thing && { @@ -339,7 +339,7 @@ const listingSpec = [ directory: artist.directory, name: artist.name, date: thing.coverArtistContribs?.some( - ({ who }) => who === artist + ({who}) => who === artist ) ? thing.coverArtDate : thing.date, @@ -357,18 +357,18 @@ const listingSpec = [ }; }, - html({ toTracks, toArtAndFlashes, showAsFlashes }, { link, language }) { + html({toTracks, toArtAndFlashes, showAsFlashes}, {link, language}) { return fixWS`

    ${language.$( - "listingPage.misc.trackContributors" + 'listingPage.misc.trackContributors' )}

      ${toTracks - .map(({ artist, date }) => + .map(({artist, date}) => language.$( - "listingPage.listArtists.byLatest.item", + 'listingPage.listArtists.byLatest.item', { artist: link.artist(artist), date: language.formatDate(date), @@ -376,21 +376,21 @@ const listingSpec = [ ) ) .map((row) => `
    • ${row}
    • `) - .join("\n")} + .join('\n')}

    ${language.$( - "listingPage.misc" + + 'listingPage.misc' + (showAsFlashes - ? ".artAndFlashContributors" - : ".artContributors") + ? '.artAndFlashContributors' + : '.artContributors') )}

      ${toArtAndFlashes - .map(({ artist, date }) => + .map(({artist, date}) => language.$( - "listingPage.listArtists.byLatest.item", + 'listingPage.listArtists.byLatest.item', { artist: link.artist(artist), date: language.formatDate(date), @@ -398,7 +398,7 @@ const listingSpec = [ ) ) .map((row) => `
    • ${row}
    • `) - .join("\n")} + .join('\n')}
    @@ -407,35 +407,35 @@ const listingSpec = [ }, { - directory: "groups/by-name", - stringsKey: "listGroups.byName", - condition: ({ wikiData }) => wikiData.wikiInfo.enableGroupUI, - data: ({ wikiData }) => sortAlphabetically(wikiData.groupData.slice()), + directory: 'groups/by-name', + stringsKey: 'listGroups.byName', + condition: ({wikiData}) => wikiData.wikiInfo.enableGroupUI, + data: ({wikiData}) => sortAlphabetically(wikiData.groupData.slice()), - row(group, { link, language }) { - return language.$("listingPage.listGroups.byCategory.group", { + row(group, {link, language}) { + return language.$('listingPage.listGroups.byCategory.group', { group: link.groupInfo(group), gallery: link.groupGallery(group, { - text: language.$("listingPage.listGroups.byCategory.group.gallery"), + text: language.$('listingPage.listGroups.byCategory.group.gallery'), }), }); }, }, { - directory: "groups/by-category", - stringsKey: "listGroups.byCategory", - condition: ({ wikiData }) => wikiData.wikiInfo.enableGroupUI, - data: ({ wikiData }) => wikiData.groupCategoryData, + directory: 'groups/by-category', + stringsKey: 'listGroups.byCategory', + condition: ({wikiData}) => wikiData.wikiInfo.enableGroupUI, + data: ({wikiData}) => wikiData.groupCategoryData, - html(groupCategoryData, { link, language }) { + html(groupCategoryData, {link, language}) { return fixWS`
    ${groupCategoryData .map( (category) => fixWS`
    ${language.$( - "listingPage.listGroups.byCategory.category", + 'listingPage.listGroups.byCategory.category', { category: link.groupInfo(category.groups[0], { text: category.name, @@ -446,53 +446,53 @@ const listingSpec = [ ${category.groups .map((group) => language.$( - "listingPage.listGroups.byCategory.group", + 'listingPage.listGroups.byCategory.group', { group: link.groupInfo(group), gallery: link.groupGallery(group, { text: language.$( - "listingPage.listGroups.byCategory.group.gallery" + 'listingPage.listGroups.byCategory.group.gallery' ), }), } ) ) .map((row) => `
  • ${row}
  • `) - .join("\n")} + .join('\n')} ` ) - .join("\n")} + .join('\n')}
    `; }, }, { - directory: "groups/by-albums", - stringsKey: "listGroups.byAlbums", - condition: ({ wikiData }) => wikiData.wikiInfo.enableGroupUI, + directory: 'groups/by-albums', + stringsKey: 'listGroups.byAlbums', + condition: ({wikiData}) => wikiData.wikiInfo.enableGroupUI, - data({ wikiData }) { + data({wikiData}) { return wikiData.groupData - .map((group) => ({ group, albums: group.albums.length })) + .map((group) => ({group, albums: group.albums.length})) .sort((a, b) => b.albums - a.albums); }, - row({ group, albums }, { link, language }) { - return language.$("listingPage.listGroups.byAlbums.item", { + row({group, albums}, {link, language}) { + return language.$('listingPage.listGroups.byAlbums.item', { group: link.groupInfo(group), - albums: language.countAlbums(albums, { unit: true }), + albums: language.countAlbums(albums, {unit: true}), }); }, }, { - directory: "groups/by-tracks", - stringsKey: "listGroups.byTracks", - condition: ({ wikiData }) => wikiData.wikiInfo.enableGroupUI, + directory: 'groups/by-tracks', + stringsKey: 'listGroups.byTracks', + condition: ({wikiData}) => wikiData.wikiInfo.enableGroupUI, - data({ wikiData }) { + data({wikiData}) { return wikiData.groupData .map((group) => ({ group, @@ -504,20 +504,20 @@ const listingSpec = [ .sort((a, b) => b.tracks - a.tracks); }, - row({ group, tracks }, { link, language }) { - return language.$("listingPage.listGroups.byTracks.item", { + row({group, tracks}, {link, language}) { + return language.$('listingPage.listGroups.byTracks.item', { group: link.groupInfo(group), - tracks: language.countTracks(tracks, { unit: true }), + tracks: language.countTracks(tracks, {unit: true}), }); }, }, { - directory: "groups/by-duration", - stringsKey: "listGroups.byDuration", - condition: ({ wikiData }) => wikiData.wikiInfo.enableGroupUI, + directory: 'groups/by-duration', + stringsKey: 'listGroups.byDuration', + condition: ({wikiData}) => wikiData.wikiInfo.enableGroupUI, - data({ wikiData }) { + data({wikiData}) { return wikiData.groupData .map((group) => ({ group, @@ -528,8 +528,8 @@ const listingSpec = [ .sort((a, b) => b.duration - a.duration); }, - row({ group, duration }, { link, language }) { - return language.$("listingPage.listGroups.byDuration.item", { + row({group, duration}, {link, language}) { + return language.$('listingPage.listGroups.byDuration.item', { group: link.groupInfo(group), duration: language.formatDuration(duration), }); @@ -537,11 +537,11 @@ const listingSpec = [ }, { - directory: "groups/by-latest-album", - stringsKey: "listGroups.byLatest", - condition: ({ wikiData }) => wikiData.wikiInfo.enableGroupUI, + directory: 'groups/by-latest-album', + stringsKey: 'listGroups.byLatest', + condition: ({wikiData}) => wikiData.wikiInfo.enableGroupUI, - data({ wikiData }) { + data({wikiData}) { return sortChronologically( wikiData.groupData .map((group) => { @@ -572,8 +572,8 @@ const listingSpec = [ ).reverse(); }, - row({ group, date }, { link, language }) { - return language.$("listingPage.listGroups.byLatest.item", { + row({group, date}, {link, language}) { + return language.$('listingPage.listGroups.byLatest.item', { group: link.groupInfo(group), date: language.formatDate(date), }); @@ -581,33 +581,33 @@ const listingSpec = [ }, { - directory: "tracks/by-name", - stringsKey: "listTracks.byName", + directory: 'tracks/by-name', + stringsKey: 'listTracks.byName', - data({ wikiData }) { + data({wikiData}) { return sortAlphabetically(wikiData.trackData.slice()); }, - row(track, { link, language }) { - return language.$("listingPage.listTracks.byName.item", { + row(track, {link, language}) { + return language.$('listingPage.listTracks.byName.item', { track: link.track(track), }); }, }, { - directory: "tracks/by-album", - stringsKey: "listTracks.byAlbum", - data: ({ wikiData }) => wikiData.albumData, + directory: 'tracks/by-album', + stringsKey: 'listTracks.byAlbum', + data: ({wikiData}) => wikiData.albumData, - html(albumData, { link, language }) { + html(albumData, {link, language}) { return fixWS`
    ${albumData .map( (album) => fixWS`
    ${language.$( - "listingPage.listTracks.byAlbum.album", + 'listingPage.listTracks.byAlbum.album', { album: link.album(album), } @@ -616,42 +616,42 @@ const listingSpec = [ ${album.tracks .map((track) => language.$( - "listingPage.listTracks.byAlbum.track", + 'listingPage.listTracks.byAlbum.track', { track: link.track(track), } ) ) .map((row) => `
  • ${row}
  • `) - .join("\n")} + .join('\n')} ` ) - .join("\n")} + .join('\n')}
    `; }, }, { - directory: "tracks/by-date", - stringsKey: "listTracks.byDate", + directory: 'tracks/by-date', + stringsKey: 'listTracks.byDate', - data({ wikiData }) { + data({wikiData}) { return chunkByProperties( sortChronologically(wikiData.trackData.filter((t) => t.date)), - ["album", "date"] + ['album', 'date'] ); }, - html(chunks, { link, language }) { + html(chunks, {link, language}) { return fixWS`
    ${chunks .map( - ({ album, date, chunk: tracks }) => fixWS` + ({album, date, chunk: tracks}) => fixWS`
    ${language.$( - "listingPage.listTracks.byDate.album", + 'listingPage.listTracks.byDate.album', { album: link.album(album), date: language.formatDate(date), @@ -662,41 +662,41 @@ const listingSpec = [ .map((track) => track.aka ? `
  • ${language.$( - "listingPage.listTracks.byDate.track.rerelease", + 'listingPage.listTracks.byDate.track.rerelease', { track: link.track(track), } )}
  • ` : `
  • ${language.$( - "listingPage.listTracks.byDate.track", + 'listingPage.listTracks.byDate.track', { track: link.track(track), } )}
  • ` ) - .join("\n")} + .join('\n')} ` ) - .join("\n")} + .join('\n')}
    `; }, }, { - directory: "tracks/by-duration", - stringsKey: "listTracks.byDuration", + directory: 'tracks/by-duration', + stringsKey: 'listTracks.byDuration', - data({ wikiData }) { + data({wikiData}) { return wikiData.trackData - .map((track) => ({ track, duration: track.duration })) - .filter(({ duration }) => duration > 0) + .map((track) => ({track, duration: track.duration})) + .filter(({duration}) => duration > 0) .sort((a, b) => b.duration - a.duration); }, - row({ track, duration }, { link, language }) { - return language.$("listingPage.listTracks.byDuration.item", { + row({track, duration}, {link, language}) { + return language.$('listingPage.listTracks.byDuration.item', { track: link.track(track), duration: language.formatDuration(duration), }); @@ -704,10 +704,10 @@ const listingSpec = [ }, { - directory: "tracks/by-duration-in-album", - stringsKey: "listTracks.byDurationInAlbum", + directory: 'tracks/by-duration-in-album', + stringsKey: 'listTracks.byDurationInAlbum', - data({ wikiData }) { + data({wikiData}) { return wikiData.albumData.map((album) => ({ album, tracks: album.tracks @@ -716,14 +716,14 @@ const listingSpec = [ })); }, - html(albums, { link, language }) { + html(albums, {link, language}) { return fixWS`
    ${albums .map( - ({ album, tracks }) => fixWS` + ({album, tracks}) => fixWS`
    ${language.$( - "listingPage.listTracks.byDurationInAlbum.album", + 'listingPage.listTracks.byDurationInAlbum.album', { album: link.album(album), } @@ -732,7 +732,7 @@ const listingSpec = [ ${tracks .map((track) => language.$( - "listingPage.listTracks.byDurationInAlbum.track", + 'listingPage.listTracks.byDurationInAlbum.track', { track: link.track(track), duration: language.formatDuration( @@ -742,32 +742,32 @@ const listingSpec = [ ) ) .map((row) => `
  • ${row}
  • `) - .join("\n")} + .join('\n')} ` ) - .join("\n")} + .join('\n')}
    `; }, }, { - directory: "tracks/by-times-referenced", - stringsKey: "listTracks.byTimesReferenced", + directory: 'tracks/by-times-referenced', + stringsKey: 'listTracks.byTimesReferenced', - data({ wikiData }) { + data({wikiData}) { return wikiData.trackData .map((track) => ({ track, timesReferenced: track.referencedByTracks.length, })) - .filter(({ timesReferenced }) => timesReferenced > 0) + .filter(({timesReferenced}) => timesReferenced > 0) .sort((a, b) => b.timesReferenced - a.timesReferenced); }, - row({ track, timesReferenced }, { link, language }) { - return language.$("listingPage.listTracks.byTimesReferenced.item", { + row({track, timesReferenced}, {link, language}) { + return language.$('listingPage.listTracks.byTimesReferenced.item', { track: link.track(track), timesReferenced: language.countTimesReferenced(timesReferenced, { unit: true, @@ -777,25 +777,25 @@ const listingSpec = [ }, { - directory: "tracks/in-flashes/by-album", - stringsKey: "listTracks.inFlashes.byAlbum", - condition: ({ wikiData }) => wikiData.wikiInfo.enableFlashesAndGames, + directory: 'tracks/in-flashes/by-album', + stringsKey: 'listTracks.inFlashes.byAlbum', + condition: ({wikiData}) => wikiData.wikiInfo.enableFlashesAndGames, - data({ wikiData }) { + data({wikiData}) { return chunkByProperties( wikiData.trackData.filter((t) => t.featuredInFlashes?.length > 0), - ["album"] + ['album'] ); }, - html(chunks, { link, language }) { + html(chunks, {link, language}) { return fixWS`
    ${chunks .map( - ({ album, chunk: tracks }) => fixWS` + ({album, chunk: tracks}) => fixWS`
    ${language.$( - "listingPage.listTracks.inFlashes.byAlbum.album", + 'listingPage.listTracks.inFlashes.byAlbum.album', { album: link.album(album), date: language.formatDate(album.date), @@ -805,7 +805,7 @@ const listingSpec = [ ${tracks .map((track) => language.$( - "listingPage.listTracks.inFlashes.byAlbum.track", + 'listingPage.listTracks.inFlashes.byAlbum.track', { track: link.track(track), flashes: language.formatConjunctionList( @@ -815,30 +815,30 @@ const listingSpec = [ ) ) .map((row) => `
  • ${row}
  • `) - .join("\n")} + .join('\n')} ` ) - .join("\n")} + .join('\n')}
    `; }, }, { - directory: "tracks/in-flashes/by-flash", - stringsKey: "listTracks.inFlashes.byFlash", - condition: ({ wikiData }) => wikiData.wikiInfo.enableFlashesAndGames, - data: ({ wikiData }) => wikiData.flashData, + directory: 'tracks/in-flashes/by-flash', + stringsKey: 'listTracks.inFlashes.byFlash', + condition: ({wikiData}) => wikiData.wikiInfo.enableFlashesAndGames, + data: ({wikiData}) => wikiData.flashData, - html(flashData, { link, language }) { + html(flashData, {link, language}) { return fixWS`
    ${sortChronologically(flashData.slice()) .map( (flash) => fixWS`
    ${language.$( - "listingPage.listTracks.inFlashes.byFlash.flash", + 'listingPage.listTracks.inFlashes.byFlash.flash', { flash: link.flash(flash), date: language.formatDate(flash.date), @@ -848,7 +848,7 @@ const listingSpec = [ ${flash.featuredTracks .map((track) => language.$( - "listingPage.listTracks.inFlashes.byFlash.track", + 'listingPage.listTracks.inFlashes.byFlash.track', { track: link.track(track), album: link.album(track.album), @@ -856,37 +856,37 @@ const listingSpec = [ ) ) .map((row) => `
  • ${row}
  • `) - .join("\n")} + .join('\n')} ` ) - .join("\n")} + .join('\n')}
    `; }, }, { - directory: "tracks/with-lyrics", - stringsKey: "listTracks.withLyrics", + directory: 'tracks/with-lyrics', + stringsKey: 'listTracks.withLyrics', - data({ wikiData }) { + data({wikiData}) { return wikiData.albumData .map((album) => ({ album, tracks: album.tracks.filter((t) => t.lyrics), })) - .filter(({ tracks }) => tracks.length > 0); + .filter(({tracks}) => tracks.length > 0); }, - html(chunks, { link, language }) { + html(chunks, {link, language}) { return fixWS`
    ${chunks .map( - ({ album, tracks }) => fixWS` + ({album, tracks}) => fixWS`
    ${language.$( - "listingPage.listTracks.withLyrics.album", + 'listingPage.listTracks.withLyrics.album', { album: link.album(album), date: language.formatDate(album.date), @@ -896,74 +896,74 @@ const listingSpec = [ ${tracks .map((track) => language.$( - "listingPage.listTracks.withLyrics.track", + 'listingPage.listTracks.withLyrics.track', { track: link.track(track), } ) ) .map((row) => `
  • ${row}
  • `) - .join("\n")} + .join('\n')} ` ) - .join("\n")} + .join('\n')}
    `; }, }, { - directory: "tags/by-name", - stringsKey: "listTags.byName", - condition: ({ wikiData }) => wikiData.wikiInfo.enableArtTagUI, + directory: 'tags/by-name', + stringsKey: 'listTags.byName', + condition: ({wikiData}) => wikiData.wikiInfo.enableArtTagUI, - data({ wikiData }) { + data({wikiData}) { return sortAlphabetically( wikiData.artTagData.filter((tag) => !tag.isContentWarning) - ).map((tag) => ({ tag, timesUsed: tag.taggedInThings?.length })); + ).map((tag) => ({tag, timesUsed: tag.taggedInThings?.length})); }, - row({ tag, timesUsed }, { link, language }) { - return language.$("listingPage.listTags.byName.item", { + row({tag, timesUsed}, {link, language}) { + return language.$('listingPage.listTags.byName.item', { tag: link.tag(tag), - timesUsed: language.countTimesUsed(timesUsed, { unit: true }), + timesUsed: language.countTimesUsed(timesUsed, {unit: true}), }); }, }, { - directory: "tags/by-uses", - stringsKey: "listTags.byUses", - condition: ({ wikiData }) => wikiData.wikiInfo.enableArtTagUI, + directory: 'tags/by-uses', + stringsKey: 'listTags.byUses', + condition: ({wikiData}) => wikiData.wikiInfo.enableArtTagUI, - data({ wikiData }) { + data({wikiData}) { return wikiData.artTagData .filter((tag) => !tag.isContentWarning) - .map((tag) => ({ tag, timesUsed: tag.taggedInThings?.length })) + .map((tag) => ({tag, timesUsed: tag.taggedInThings?.length})) .sort((a, b) => b.timesUsed - a.timesUsed); }, - row({ tag, timesUsed }, { link, language }) { - return language.$("listingPage.listTags.byUses.item", { + row({tag, timesUsed}, {link, language}) { + return language.$('listingPage.listTags.byUses.item', { tag: link.tag(tag), - timesUsed: language.countTimesUsed(timesUsed, { unit: true }), + timesUsed: language.countTimesUsed(timesUsed, {unit: true}), }); }, }, { - directory: "random", - stringsKey: "other.randomPages", + directory: 'random', + stringsKey: 'other.randomPages', - data: ({ wikiData }) => ({ + data: ({wikiData}) => ({ officialAlbumData: wikiData.officialAlbumData, fandomAlbumData: wikiData.fandomAlbumData, }), html: ( - { officialAlbumData, fandomAlbumData }, - { getLinkThemeString, language } + {officialAlbumData, fandomAlbumData}, + {getLinkThemeString, language} ) => fixWS`

    Choose a link to go to a random page in that category or album! If your browser doesn't support relatively modern JavaScript or you've disabled it, these links won't work - sorry.

    (Data files are downloading in the background! Please wait for data to load.)

    @@ -980,14 +980,14 @@ const listingSpec = [ ${[ { - name: "Official", + name: 'Official', albumData: officialAlbumData, - code: "official", + code: 'official', }, { - name: "Fandom", + name: 'Fandom', albumData: fandomAlbumData, - code: "fandom", + code: 'fandom', }, ] .map( @@ -1009,10 +1009,10 @@ const listingSpec = [ } ` ) - .join("\n")} + .join('\n')} ` ) - .join("\n")} + .join('\n')} `, }, @@ -1023,29 +1023,29 @@ const filterListings = (directoryPrefix) => const listingTargetSpec = [ { - title: ({ language }) => language.$("listingPage.target.album"), - listings: filterListings("album"), + title: ({language}) => language.$('listingPage.target.album'), + listings: filterListings('album'), }, { - title: ({ language }) => language.$("listingPage.target.artist"), - listings: filterListings("artist"), + title: ({language}) => language.$('listingPage.target.artist'), + listings: filterListings('artist'), }, { - title: ({ language }) => language.$("listingPage.target.group"), - listings: filterListings("group"), + title: ({language}) => language.$('listingPage.target.group'), + listings: filterListings('group'), }, { - title: ({ language }) => language.$("listingPage.target.track"), - listings: filterListings("track"), + title: ({language}) => language.$('listingPage.target.track'), + listings: filterListings('track'), }, { - title: ({ language }) => language.$("listingPage.target.tag"), - listings: filterListings("tag"), + title: ({language}) => language.$('listingPage.target.tag'), + listings: filterListings('tag'), }, { - title: ({ language }) => language.$("listingPage.target.other"), - listings: [listingSpec.find((l) => l.directory === "random")], + title: ({language}) => language.$('listingPage.target.other'), + listings: [listingSpec.find((l) => l.directory === 'random')], }, ]; -export { listingSpec, listingTargetSpec }; +export {listingSpec, listingTargetSpec}; diff --git a/src/misc-templates.js b/src/misc-templates.js index f0e0edf..27f7018 100644 --- a/src/misc-templates.js +++ b/src/misc-templates.js @@ -1,37 +1,37 @@ -// @format -// +/** @format */ + // Miscellaneous utility functions which are useful across page specifications. // These are made available right on a page spec's ({wikiData, language, ...}) // args object! -import fixWS from "fix-whitespace"; +import fixWS from 'fix-whitespace'; -import * as html from "./util/html.js"; +import * as html from './util/html.js'; -import { Track, Album } from "./data/things.js"; +import {Track, Album} from './data/things.js'; -import { getColors } from "./util/colors.js"; +import {getColors} from './util/colors.js'; -import { unique } from "./util/sugar.js"; +import {unique} from './util/sugar.js'; import { getTotalDuration, sortAlbumsTracksChronologically, sortChronologically, -} from "./util/wiki-data.js"; +} from './util/wiki-data.js'; -const BANDCAMP_DOMAINS = ["bc.s3m.us", "music.solatrux.com"]; +const BANDCAMP_DOMAINS = ['bc.s3m.us', 'music.solatrux.com']; -const MASTODON_DOMAINS = ["types.pl"]; +const MASTODON_DOMAINS = ['types.pl']; // "Additional Files" listing -export function generateAdditionalFilesShortcut(additionalFiles, { language }) { - if (!additionalFiles?.length) return ""; +export function generateAdditionalFilesShortcut(additionalFiles, {language}) { + if (!additionalFiles?.length) return ''; - return language.$("releaseInfo.additionalFiles.shortcut", { + return language.$('releaseInfo.additionalFiles.shortcut', { anchorLink: `${language.$( - "releaseInfo.additionalFiles.shortcut.anchorLink" + 'releaseInfo.additionalFiles.shortcut.anchorLink' )}`, titles: language.formatUnitList(additionalFiles.map((g) => g.title)), }); @@ -39,15 +39,15 @@ export function generateAdditionalFilesShortcut(additionalFiles, { language }) { export function generateAdditionalFilesList( additionalFiles, - { language, getFileSize, linkFile } + {language, getFileSize, linkFile} ) { - if (!additionalFiles?.length) return ""; + if (!additionalFiles?.length) return ''; const fileCount = additionalFiles.flatMap((g) => g.files).length; return fixWS`

    ${language.$( - "releaseInfo.additionalFiles.heading", + 'releaseInfo.additionalFiles.heading', { additionalFiles: language.countAdditionalFiles(fileCount, { unit: true, @@ -57,14 +57,14 @@ export function generateAdditionalFilesList(

    ${additionalFiles .map( - ({ title, description, files }) => fixWS` + ({title, description, files}) => fixWS`
    ${ description ? language.$( - "releaseInfo.additionalFiles.entry.withDescription", - { title, description } + 'releaseInfo.additionalFiles.entry.withDescription', + {title, description} ) - : language.$("releaseInfo.additionalFiles.entry", { title }) + : language.$('releaseInfo.additionalFiles.entry', {title}) }
      ${files @@ -72,7 +72,7 @@ export function generateAdditionalFilesList( const size = getFileSize(file); return size ? `
    • ${language.$( - "releaseInfo.additionalFiles.file.withSize", + 'releaseInfo.additionalFiles.file.withSize', { file: linkFile(file), size: language.formatFileSize( @@ -81,17 +81,17 @@ export function generateAdditionalFilesList( } )}
    • ` : `
    • ${language.$( - "releaseInfo.additionalFiles.file", + 'releaseInfo.additionalFiles.file', { file: linkFile(file), } )}
    • `; }) - .join("\n")} + .join('\n')}
    ` ) - .join("\n")} + .join('\n')}
    `; } @@ -100,22 +100,22 @@ export function generateAdditionalFilesList( export function getArtistString( artists, - { iconifyURL, link, language, showIcons = false, showContrib = false } + {iconifyURL, link, language, showIcons = false, showContrib = false} ) { return language.formatConjunctionList( - artists.map(({ who, what }) => { - const { urls, directory, name } = who; + artists.map(({who, what}) => { + const {urls, directory, name} = who; return [ link.artist(who), showContrib && what && `(${what})`, showIcons && urls?.length && `(${language.formatUnitList( - urls.map((url) => iconifyURL(url, { language })) + urls.map((url) => iconifyURL(url, {language})) )})`, ] .filter(Boolean) - .join(" "); + .join(' '); }) ); } @@ -125,7 +125,7 @@ export function getArtistString( export function generateChronologyLinks( currentThing, { - dateKey = "date", + dateKey = 'date', contribKey, getThings, headingString, @@ -135,28 +135,28 @@ export function generateChronologyLinks( wikiData, } ) { - const { albumData } = wikiData; + const {albumData} = wikiData; const contributions = currentThing[contribKey]; if (!contributions) { - return ""; + return ''; } if (contributions.length > 8) { return `
    ${language.$( - "misc.chronology.seeArtistPages" + 'misc.chronology.seeArtistPages' )}
    `; } return contributions - .map(({ who: artist }) => { + .map(({who: artist}) => { const thingsUnsorted = unique(getThings(artist)).filter( (t) => t[dateKey] ); // Kinda a hack, but we automatically detect which is (probably) the // right function to use here. - const args = [thingsUnsorted, { getDate: (t) => t[dateKey] }]; + const args = [thingsUnsorted, {getDate: (t) => t[dateKey]}]; const things = thingsUnsorted.every( (t) => t instanceof Album || t instanceof Track ) @@ -165,7 +165,7 @@ export function generateChronologyLinks( const index = things.indexOf(currentThing); - if (index === -1) return ""; + if (index === -1) return ''; // TODO: This can pro8a8ly 8e made to use generatePreviousNextLinks? // We'd need to make generatePreviousNextLinks use toAnythingMan tho. @@ -175,21 +175,21 @@ export function generateChronologyLinks( previous && linkAnythingMan(previous, { color: false, - text: language.$("misc.nav.previous"), + text: language.$('misc.nav.previous'), }), next && linkAnythingMan(next, { color: false, - text: language.$("misc.nav.next"), + text: language.$('misc.nav.next'), }), ].filter(Boolean); if (!parts.length) { - return ""; + return ''; } const stringOpts = { - index: language.formatIndex(index + 1, { language }), + index: language.formatIndex(index + 1, {language}), artist: link.artist(artist), }; @@ -201,27 +201,27 @@ export function generateChronologyLinks( )} ${ parts.length && - `(${parts.join(", ")})` + `(${parts.join(', ')})` } `; }) .filter(Boolean) - .join("\n"); + .join('\n'); } // Content warning tags -export function getRevealStringFromWarnings(warnings, { language }) { +export function getRevealStringFromWarnings(warnings, {language}) { return ( - language.$("misc.contentWarnings", { warnings }) + + language.$('misc.contentWarnings', {warnings}) + `
    ${language.$( - "misc.contentWarnings.reveal" + 'misc.contentWarnings.reveal' )}` ); } -export function getRevealStringFromTags(tags, { language }) { +export function getRevealStringFromTags(tags, {language}) { return ( tags && tags.some((tag) => tag.isContentWarning) && @@ -229,7 +229,7 @@ export function getRevealStringFromTags(tags, { language }) { language.formatUnitList( tags.filter((tag) => tag.isContentWarning).map((tag) => tag.name) ), - { language } + {language} ) ); } @@ -247,7 +247,7 @@ export function generateCoverLink({ alt, tags = [], }) { - const { wikiInfo } = wikiData; + const {wikiInfo} = wikiData; if (!src && path) { src = to(...path); @@ -262,22 +262,22 @@ export function generateCoverLink({ ${img({ src, alt, - thumb: "medium", - id: "cover-art", + thumb: 'medium', + id: 'cover-art', link: true, square: true, - reveal: getRevealStringFromTags(tags, { language }), + reveal: getRevealStringFromTags(tags, {language}), })} ${ wikiInfo.enableArtTagUI && tags.filter((tag) => !tag.isContentWarning).length && fixWS`

    - ${language.$("releaseInfo.artTags")} + ${language.$('releaseInfo.artTags')} ${tags .filter((tag) => !tag.isContentWarning) .map(link.tag) - .join(",\n")} + .join(',\n')}

    ` } @@ -288,9 +288,9 @@ export function generateCoverLink({ // CSS & color shenanigans export function getThemeString(color, additionalVariables = []) { - if (!color) return ""; + if (!color) return ''; - const { primary, dim, bg } = getColors(color); + const {primary, dim, bg} = getColors(color); const variables = [ `--primary-color: ${primary}`, @@ -299,19 +299,19 @@ export function getThemeString(color, additionalVariables = []) { ...additionalVariables, ].filter(Boolean); - if (!variables.length) return ""; + if (!variables.length) return ''; return ( - `:root {\n` + variables.map((line) => ` ` + line + ";\n").join("") + `}` + `:root {\n` + variables.map((line) => ` ` + line + ';\n').join('') + `}` ); } -export function getAlbumStylesheet(album, { to }) { +export function getAlbumStylesheet(album, {to}) { return [ album.wallpaperArtistContribs.length && fixWS` body::before { background-image: url("${to( - "media.albumWallpaper", + 'media.albumWallpaper', album.directory, album.wallpaperFileExtension )}"); @@ -326,31 +326,31 @@ export function getAlbumStylesheet(album, { to }) { `, ] .filter(Boolean) - .join("\n"); + .join('\n'); } // Divided track lists export function generateTrackListDividedByGroups( tracks, - { getTrackItem, language, wikiData } + {getTrackItem, language, wikiData} ) { - const { divideTrackListsByGroups: groups } = wikiData.wikiInfo; + const {divideTrackListsByGroups: groups} = wikiData.wikiInfo; if (!groups?.length) { return html.tag( - "ul", + 'ul', tracks.map((t) => getTrackItem(t)) ); } const lists = Object.fromEntries( - groups.map((group) => [group.directory, { group, tracks: [] }]) + groups.map((group) => [group.directory, {group, tracks: []}]) ); const other = []; for (const track of tracks) { - const { album } = track; + const {album} = track; const group = groups.find((g) => g.albums.includes(album)); if (group) { lists[group.directory].tracks.push(track); @@ -361,26 +361,26 @@ export function generateTrackListDividedByGroups( const ddul = (tracks) => fixWS`
      - ${tracks.map((t) => getTrackItem(t)).join("\n")} + ${tracks.map((t) => getTrackItem(t)).join('\n')}
    `; return html.tag( - "dl", + 'dl', Object.values(lists) - .filter(({ tracks }) => tracks.length) - .flatMap(({ group, tracks }) => [ + .filter(({tracks}) => tracks.length) + .flatMap(({group, tracks}) => [ html.tag( - "dt", - language.formatString("trackList.group", { group: group.name }) + 'dt', + language.formatString('trackList.group', {group: group.name}) ), ddul(tracks), ]) .concat( other.length ? [ - `
    ${language.formatString("trackList.group", { - group: language.formatString("trackList.group.other"), + `
    ${language.formatString('trackList.group', { + group: language.formatString('trackList.group.other'), })}
    `, ddul(other), ] @@ -391,7 +391,7 @@ export function generateTrackListDividedByGroups( // Fancy lookin' links -export function fancifyURL(url, { language, album = false } = {}) { +export function fancifyURL(url, {language, album = false} = {}) { let local = Symbol(); let domain; try { @@ -403,80 +403,80 @@ export function fancifyURL(url, { language, album = false } = {}) { } return fixWS`${ domain === local - ? language.$("misc.external.local") - : domain.includes("bandcamp.com") - ? language.$("misc.external.bandcamp") + ? language.$('misc.external.local') + : domain.includes('bandcamp.com') + ? language.$('misc.external.bandcamp') : BANDCAMP_DOMAINS.includes(domain) - ? language.$("misc.external.bandcamp.domain", { domain }) + ? language.$('misc.external.bandcamp.domain', {domain}) : MASTODON_DOMAINS.includes(domain) - ? language.$("misc.external.mastodon.domain", { domain }) - : domain.includes("youtu") + ? language.$('misc.external.mastodon.domain', {domain}) + : domain.includes('youtu') ? album - ? url.includes("list=") - ? language.$("misc.external.youtube.playlist") - : language.$("misc.external.youtube.fullAlbum") - : language.$("misc.external.youtube") - : domain.includes("soundcloud") - ? language.$("misc.external.soundcloud") - : domain.includes("tumblr.com") - ? language.$("misc.external.tumblr") - : domain.includes("twitter.com") - ? language.$("misc.external.twitter") - : domain.includes("deviantart.com") - ? language.$("misc.external.deviantart") - : domain.includes("wikipedia.org") - ? language.$("misc.external.wikipedia") - : domain.includes("poetryfoundation.org") - ? language.$("misc.external.poetryFoundation") - : domain.includes("instagram.com") - ? language.$("misc.external.instagram") - : domain.includes("patreon.com") - ? language.$("misc.external.patreon") + ? url.includes('list=') + ? language.$('misc.external.youtube.playlist') + : language.$('misc.external.youtube.fullAlbum') + : language.$('misc.external.youtube') + : domain.includes('soundcloud') + ? language.$('misc.external.soundcloud') + : domain.includes('tumblr.com') + ? language.$('misc.external.tumblr') + : domain.includes('twitter.com') + ? language.$('misc.external.twitter') + : domain.includes('deviantart.com') + ? language.$('misc.external.deviantart') + : domain.includes('wikipedia.org') + ? language.$('misc.external.wikipedia') + : domain.includes('poetryfoundation.org') + ? language.$('misc.external.poetryFoundation') + : domain.includes('instagram.com') + ? language.$('misc.external.instagram') + : domain.includes('patreon.com') + ? language.$('misc.external.patreon') : domain }`; } -export function fancifyFlashURL(url, flash, { language }) { - const link = fancifyURL(url, { language }); +export function fancifyFlashURL(url, flash, {language}) { + const link = fancifyURL(url, {language}); return `${ - url.includes("homestuck.com") + url.includes('homestuck.com') ? isNaN(Number(flash.page)) - ? language.$("misc.external.flash.homestuck.secret", { link }) - : language.$("misc.external.flash.homestuck.page", { + ? language.$('misc.external.flash.homestuck.secret', {link}) + : language.$('misc.external.flash.homestuck.page', { link, page: flash.page, }) - : url.includes("bgreco.net") - ? language.$("misc.external.flash.bgreco", { link }) - : url.includes("youtu") - ? language.$("misc.external.flash.youtube", { link }) + : url.includes('bgreco.net') + ? language.$('misc.external.flash.bgreco', {link}) + : url.includes('youtu') + ? language.$('misc.external.flash.youtube', {link}) : link }`; } -export function iconifyURL(url, { language, to }) { +export function iconifyURL(url, {language, to}) { const domain = new URL(url).hostname; - const [id, msg] = domain.includes("bandcamp.com") - ? ["bandcamp", language.$("misc.external.bandcamp")] + const [id, msg] = domain.includes('bandcamp.com') + ? ['bandcamp', language.$('misc.external.bandcamp')] : BANDCAMP_DOMAINS.includes(domain) - ? ["bandcamp", language.$("misc.external.bandcamp.domain", { domain })] + ? ['bandcamp', language.$('misc.external.bandcamp.domain', {domain})] : MASTODON_DOMAINS.includes(domain) - ? ["mastodon", language.$("misc.external.mastodon.domain", { domain })] - : domain.includes("youtu") - ? ["youtube", language.$("misc.external.youtube")] - : domain.includes("soundcloud") - ? ["soundcloud", language.$("misc.external.soundcloud")] - : domain.includes("tumblr.com") - ? ["tumblr", language.$("misc.external.tumblr")] - : domain.includes("twitter.com") - ? ["twitter", language.$("misc.external.twitter")] - : domain.includes("deviantart.com") - ? ["deviantart", language.$("misc.external.deviantart")] - : domain.includes("instagram.com") - ? ["instagram", language.$("misc.external.bandcamp")] - : ["globe", language.$("misc.external.domain", { domain })]; + ? ['mastodon', language.$('misc.external.mastodon.domain', {domain})] + : domain.includes('youtu') + ? ['youtube', language.$('misc.external.youtube')] + : domain.includes('soundcloud') + ? ['soundcloud', language.$('misc.external.soundcloud')] + : domain.includes('tumblr.com') + ? ['tumblr', language.$('misc.external.tumblr')] + : domain.includes('twitter.com') + ? ['twitter', language.$('misc.external.twitter')] + : domain.includes('deviantart.com') + ? ['deviantart', language.$('misc.external.deviantart')] + : domain.includes('instagram.com') + ? ['instagram', language.$('misc.external.bandcamp')] + : ['globe', language.$('misc.external.domain', {domain})]; return fixWS`${msg}`; } @@ -490,23 +490,23 @@ export function getGridHTML({ entries, srcFn, linkFn, - noSrcTextFn = () => "", - altFn = () => "", + noSrcTextFn = () => '', + altFn = () => '', detailsFn = null, lazy = true, }) { return entries - .map(({ large, item }, i) => + .map(({large, item}, i) => linkFn(item, { - class: ["grid-item", "box", large && "large-grid-item"], + class: ['grid-item', 'box', large && 'large-grid-item'], text: fixWS` ${img({ src: srcFn(item), alt: altFn(item), - thumb: "small", - lazy: typeof lazy === "number" ? i >= lazy : lazy, + thumb: 'small', + lazy: typeof lazy === 'number' ? i >= lazy : lazy, square: true, - reveal: getRevealStringFromTags(item.artTags, { language }), + reveal: getRevealStringFromTags(item.artTags, {language}), noSrcText: noSrcTextFn(item), })} ${item.name} @@ -514,7 +514,7 @@ export function getGridHTML({ `, }) ) - .join("\n"); + .join('\n'); } export function getAlbumGridHTML({ @@ -531,24 +531,19 @@ export function getAlbumGridHTML({ detailsFn: details && ((album) => - language.$("misc.albumGrid.details", { - tracks: language.countTracks(album.tracks.length, { unit: true }), + language.$('misc.albumGrid.details', { + tracks: language.countTracks(album.tracks.length, {unit: true}), time: language.formatDuration(getTotalDuration(album.tracks)), })), noSrcTextFn: (album) => - language.$("misc.albumGrid.noCoverArt", { + language.$('misc.albumGrid.noCoverArt', { album: album.name, }), ...props, }); } -export function getFlashGridHTML({ - getFlashCover, - getGridHTML, - link, - ...props -}) { +export function getFlashGridHTML({getFlashCover, getGridHTML, link, ...props}) { return getGridHTML({ srcFn: getFlashCover, linkFn: link.flash, @@ -561,23 +556,23 @@ export function getFlashGridHTML({ export function generateInfoGalleryLinks( currentThing, isGallery, - { link, language, linkKeyGallery, linkKeyInfo } + {link, language, linkKeyGallery, linkKeyInfo} ) { return [ link[linkKeyInfo](currentThing, { - class: isGallery ? "" : "current", - text: language.$("misc.nav.info"), + class: isGallery ? '' : 'current', + text: language.$('misc.nav.info'), }), link[linkKeyGallery](currentThing, { - class: isGallery ? "current" : "", - text: language.$("misc.nav.gallery"), + class: isGallery ? 'current' : '', + text: language.$('misc.nav.gallery'), }), - ].join(", "); + ].join(', '); } export function generatePreviousNextLinks( current, - { data, link, linkKey, language } + {data, link, linkKey, language} ) { const linkFn = link[linkKey]; @@ -589,51 +584,51 @@ export function generatePreviousNextLinks( previous && linkFn(previous, { attributes: { - id: "previous-button", + id: 'previous-button', title: previous.name, }, - text: language.$("misc.nav.previous"), + text: language.$('misc.nav.previous'), color: false, }), next && linkFn(next, { attributes: { - id: "next-button", + id: 'next-button', title: next.name, }, - text: language.$("misc.nav.next"), + text: language.$('misc.nav.next'), color: false, }), ] .filter(Boolean) - .join(", "); + .join(', '); } // Footer stuff export function getFooterLocalizationLinks( pathname, - { defaultLanguage, languages, paths, language, to } + {defaultLanguage, languages, paths, language, to} ) { - const { toPath } = paths; - const keySuffix = toPath[0].replace(/^localized\./, "."); + const {toPath} = paths; + const keySuffix = toPath[0].replace(/^localized\./, '.'); const toArgs = toPath.slice(1); const links = Object.entries(languages) - .filter(([code, language]) => code !== "default" && !language.hidden) + .filter(([code, language]) => code !== 'default' && !language.hidden) .map(([code, language]) => language) - .sort(({ name: a }, { name: b }) => (a < b ? -1 : a > b ? 1 : 0)) + .sort(({name: a}, {name: b}) => (a < b ? -1 : a > b ? 1 : 0)) .map((language) => html.tag( - "span", + 'span', html.tag( - "a", + 'a', { href: language === defaultLanguage - ? to("localizedDefaultLanguage" + keySuffix, ...toArgs) + ? to('localizedDefaultLanguage' + keySuffix, ...toArgs) : to( - "localizedWithBaseDirectory" + keySuffix, + 'localizedWithBaseDirectory' + keySuffix, language.code, ...toArgs ), @@ -644,8 +639,8 @@ export function getFooterLocalizationLinks( ); return html.tag( - "div", - { class: "footer-localization-links" }, - language.$("misc.uiLanguage", { languages: links.join("\n") }) + 'div', + {class: 'footer-localization-links'}, + language.$('misc.uiLanguage', {languages: links.join('\n')}) ); } diff --git a/src/page/album-commentary.js b/src/page/album-commentary.js index b469838..719bd65 100644 --- a/src/page/album-commentary.js +++ b/src/page/album-commentary.js @@ -1,34 +1,34 @@ -// @format -// +/** @format */ + // Album commentary page and index specifications. // Imports -import fixWS from "fix-whitespace"; +import fixWS from 'fix-whitespace'; -import { filterAlbumsByCommentary } from "../util/wiki-data.js"; +import {filterAlbumsByCommentary} from '../util/wiki-data.js'; // Page exports -export function condition({ wikiData }) { +export function condition({wikiData}) { return filterAlbumsByCommentary(wikiData.albumData).length; } -export function targets({ wikiData }) { +export function targets({wikiData}) { return filterAlbumsByCommentary(wikiData.albumData); } -export function write(album, { wikiData }) { - const { wikiInfo } = wikiData; +export function write(album, {wikiData}) { + const {wikiInfo} = wikiData; const entries = [album, ...album.tracks] .filter((x) => x.commentary) .map((x) => x.commentary); - const words = entries.join(" ").split(" ").length; + const words = entries.join(' ').split(' ').length; const page = { - type: "page", - path: ["albumCommentary", album.directory], + type: 'page', + path: ['albumCommentary', album.directory], page: ({ getAlbumStylesheet, getLinkThemeString, @@ -38,30 +38,30 @@ export function write(album, { wikiData }) { to, transformMultiline, }) => ({ - title: language.$("albumCommentaryPage.title", { album: album.name }), + title: language.$('albumCommentaryPage.title', {album: album.name}), stylesheet: getAlbumStylesheet(album), theme: getThemeString(album.color), main: { content: fixWS`
    -

    ${language.$("albumCommentaryPage.title", { +

    ${language.$('albumCommentaryPage.title', { album: link.album(album), })}

    -

    ${language.$("albumCommentaryPage.infoLine", { +

    ${language.$('albumCommentaryPage.infoLine', { words: `${language.formatWordCount(words, { unit: true, })}`, entries: `${language.countCommentaryEntries( entries.length, - { unit: true } + {unit: true} )}`, })}

    ${ album.commentary && fixWS`

    ${language.$( - "albumCommentaryPage.entry.title.albumCommentary" + 'albumCommentaryPage.entry.title.albumCommentary' )}

    ${transformMultiline(album.commentary)} @@ -73,7 +73,7 @@ export function write(album, { wikiData }) { .map( (track) => fixWS`

    ${language.$( - "albumCommentaryPage.entry.title.trackCommentary", + 'albumCommentaryPage.entry.title.trackCommentary', { track: link.track(track), } @@ -85,22 +85,22 @@ export function write(album, { wikiData }) {

    ` ) - .join("\n")} + .join('\n')}
    `, }, nav: { - linkContainerClasses: ["nav-links-hierarchy"], + linkContainerClasses: ['nav-links-hierarchy'], links: [ - { toHome: true }, + {toHome: true}, { - path: ["localized.commentaryIndex"], - title: language.$("commentaryIndex.title"), + path: ['localized.commentaryIndex'], + title: language.$('commentaryIndex.title'), }, { - html: language.$("albumCommentaryPage.nav.album", { - album: link.albumCommentary(album, { class: "current" }), + html: language.$('albumCommentaryPage.nav.album', { + album: link.albumCommentary(album, {class: 'current'}), }), }, ], @@ -111,7 +111,7 @@ export function write(album, { wikiData }) { return [page]; } -export function writeTargetless({ wikiData }) { +export function writeTargetless({wikiData}) { const data = filterAlbumsByCommentary(wikiData.albumData) .map((album) => ({ album, @@ -119,44 +119,41 @@ export function writeTargetless({ wikiData }) { .filter((x) => x.commentary) .map((x) => x.commentary), })) - .map(({ album, entries }) => ({ + .map(({album, entries}) => ({ album, entries, - words: entries.join(" ").split(" ").length, + words: entries.join(' ').split(' ').length, })); - const totalEntries = data.reduce( - (acc, { entries }) => acc + entries.length, - 0 - ); - const totalWords = data.reduce((acc, { words }) => acc + words, 0); + const totalEntries = data.reduce((acc, {entries}) => acc + entries.length, 0); + const totalWords = data.reduce((acc, {words}) => acc + words, 0); const page = { - type: "page", - path: ["commentaryIndex"], - page: ({ link, language }) => ({ - title: language.$("commentaryIndex.title"), + type: 'page', + path: ['commentaryIndex'], + page: ({link, language}) => ({ + title: language.$('commentaryIndex.title'), main: { content: fixWS`
    -

    ${language.$("commentaryIndex.title")}

    -

    ${language.$("commentaryIndex.infoLine", { +

    ${language.$('commentaryIndex.title')}

    +

    ${language.$('commentaryIndex.infoLine', { words: `${language.formatWordCount(totalWords, { unit: true, })}`, entries: `${language.countCommentaryEntries( totalEntries, - { unit: true } + {unit: true} )}`, })}

    -

    ${language.$("commentaryIndex.albumList.title")}

    +

    ${language.$('commentaryIndex.albumList.title')}

      ${data .map( - ({ album, entries, words }) => fixWS` + ({album, entries, words}) => fixWS`
    • ${language.$( - "commentaryIndex.albumList.item", + 'commentaryIndex.albumList.item', { album: link.albumCommentary(album), words: language.formatWordCount(words, { @@ -165,19 +162,19 @@ export function writeTargetless({ wikiData }) { entries: language.countCommentaryEntries( entries.length, - { unit: true } + {unit: true} ), } )}
    • ` ) - .join("\n")} + .join('\n')}
    `, }, - nav: { simple: true }, + nav: {simple: true}, }), }; diff --git a/src/page/album.js b/src/page/album.js index f015976..6c8bb84 100644 --- a/src/page/album.js +++ b/src/page/album.js @@ -1,33 +1,33 @@ -// @format -// +/** @format */ + // Album page specification. // Imports -import fixWS from "fix-whitespace"; +import fixWS from 'fix-whitespace'; -import * as html from "../util/html.js"; +import * as html from '../util/html.js'; -import { bindOpts, compareArrays } from "../util/sugar.js"; +import {bindOpts, compareArrays} from '../util/sugar.js'; import { getAlbumCover, getAlbumListTag, getTotalDuration, -} from "../util/wiki-data.js"; +} from '../util/wiki-data.js'; // Page exports -export function targets({ wikiData }) { +export function targets({wikiData}) { return wikiData.albumData; } -export function write(album, { wikiData }) { - const { wikiInfo } = wikiData; +export function write(album, {wikiData}) { + const {wikiInfo} = wikiData; const unbound_trackToListItem = ( track, - { getArtistString, getLinkThemeString, link, language } + {getArtistString, getLinkThemeString, link, language} ) => { const itemOpts = { duration: language.formatDuration(track.duration ?? 0), @@ -37,13 +37,13 @@ export function write(album, { wikiData }) { compareArrays( track.artistContribs.map((c) => c.who), album.artistContribs.map((c) => c.who), - { checkOrder: false } + {checkOrder: false} ) - ? language.$("trackList.item.withDuration", itemOpts) - : language.$("trackList.item.withDuration.withArtists", { + ? language.$('trackList.item.withDuration', itemOpts) + : language.$('trackList.item.withDuration.withArtists', { ...itemOpts, by: `${language.$( - "trackList.item.withArtists.by", + 'trackList.item.withArtists.by', { artists: getArtistString(track.artistContribs), } @@ -60,8 +60,8 @@ export function write(album, { wikiData }) { const listTag = getAlbumListTag(album); const data = { - type: "data", - path: ["album", album.directory], + type: 'data', + path: ['album', album.directory], data: ({ serializeContribs, serializeCover, @@ -97,8 +97,8 @@ export function write(album, { wikiData }) { }; const page = { - type: "page", - path: ["album", album.directory], + type: 'page', + path: ['album', album.directory], page: ({ fancifyURL, generateAdditionalFilesShortcut, @@ -126,7 +126,7 @@ export function write(album, { wikiData }) { const cover = getAlbumCover(album); return { - title: language.$("albumPage.title", { album: album.name }), + title: language.$('albumPage.title', {album: album.name}), stylesheet: getAlbumStylesheet(album), theme: getThemeString(album.color, [ `--album-directory: ${album.directory}`, @@ -135,12 +135,12 @@ export function write(album, { wikiData }) { banner: album.bannerArtistContribs.length && { dimensions: album.bannerDimensions, path: [ - "media.albumBanner", + 'media.albumBanner', album.directory, album.bannerFileExtension, ], - alt: language.$("misc.alt.albumBanner"), - position: "top", + alt: language.$('misc.alt.albumBanner'), + position: 'top', }, main: { @@ -149,17 +149,17 @@ export function write(album, { wikiData }) { cover && generateCoverLink({ src: cover, - alt: language.$("misc.alt.albumCover"), + alt: language.$('misc.alt.albumCover'), tags: album.artTags, }) } -

    ${language.$("albumPage.title", { +

    ${language.$('albumPage.title', { album: album.name, })}

    ${[ album.artistContribs.length && - language.$("releaseInfo.by", { + language.$('releaseInfo.by', { artists: getArtistString( album.artistContribs, { @@ -169,7 +169,7 @@ export function write(album, { wikiData }) { ), }), album.coverArtistContribs.length && - language.$("releaseInfo.coverArtBy", { + language.$('releaseInfo.coverArtBy', { artists: getArtistString( album.coverArtistContribs, { @@ -179,7 +179,7 @@ export function write(album, { wikiData }) { ), }), album.wallpaperArtistContribs.length && - language.$("releaseInfo.wallpaperArtBy", { + language.$('releaseInfo.wallpaperArtBy', { artists: getArtistString( album.wallpaperArtistContribs, { @@ -189,7 +189,7 @@ export function write(album, { wikiData }) { ), }), album.bannerArtistContribs.length && - language.$("releaseInfo.bannerArtBy", { + language.$('releaseInfo.bannerArtBy', { artists: getArtistString( album.bannerArtistContribs, { @@ -199,23 +199,23 @@ export function write(album, { wikiData }) { ), }), album.date && - language.$("releaseInfo.released", { + language.$('releaseInfo.released', { date: language.formatDate(album.date), }), album.coverArtDate && +album.coverArtDate !== +album.date && - language.$("releaseInfo.artReleased", { + language.$('releaseInfo.artReleased', { date: language.formatDate(album.coverArtDate), }), - language.$("releaseInfo.duration", { + language.$('releaseInfo.duration', { duration: language.formatDuration( albumDuration, - { approximate: album.tracks.length > 1 } + {approximate: album.tracks.length > 1} ), }), ] .filter(Boolean) - .join("
    \n")} + .join('
    \n')}

    ${ (hasAdditionalFiles || hasCommentaryEntries) && @@ -224,26 +224,26 @@ export function write(album, { wikiData }) { hasAdditionalFiles && generateAdditionalFilesShortcut( album.additionalFiles, - { language } + {language} ), hasCommentaryEntries && - language.$("releaseInfo.viewCommentary", { + language.$('releaseInfo.viewCommentary', { link: link.albumCommentary(album, { text: language.$( - "releaseInfo.viewCommentary.link" + 'releaseInfo.viewCommentary.link' ), }), }), ] .filter(Boolean) - .join("
    \n")}

    ` + .join('
    \n')}

    ` } ${ album.urls?.length && - `

    ${language.$("releaseInfo.listenOn", { + `

    ${language.$('releaseInfo.listenOn', { links: language.formatDisjunctionList( album.urls.map((url) => - fancifyURL(url, { album: true }) + fancifyURL(url, {album: true}) ) ), })}

    ` @@ -263,32 +263,32 @@ export function write(album, { wikiData }) { tracks, }) => fixWS`
    ${language.$( - "trackList.section.withDuration", + 'trackList.section.withDuration', { duration: language.formatDuration( getTotalDuration(tracks), - { approximate: tracks.length > 1 } + {approximate: tracks.length > 1} ), section: name, } )}
    <${ - listTag === "ol" + listTag === 'ol' ? `ol start="${startIndex + 1}"` : listTag }> ${tracks .map(trackToListItem) - .join("\n")} + .join('\n')}
    ` ) - .join("\n")} + .join('\n')} ` : fixWS` <${listTag}> - ${album.tracks.map(trackToListItem).join("\n")} + ${album.tracks.map(trackToListItem).join('\n')} ` } @@ -297,14 +297,14 @@ export function write(album, { wikiData }) { fixWS`

    ${[ - language.$("releaseInfo.addedToWiki", { + language.$('releaseInfo.addedToWiki', { date: language.formatDate( album.dateAddedToWiki ), }), ] .filter(Boolean) - .join("
    \n")} + .join('
    \n')}

    ` } @@ -315,21 +315,21 @@ export function write(album, { wikiData }) { getFileSize: (file) => getSizeOfAdditionalFile( urls - .from("media.root") + .from('media.root') .to( - "media.albumAdditionalFile", + 'media.albumAdditionalFile', album.directory, file ) ), linkFile: (file) => - link.albumAdditionalFile({ album, file }), + link.albumAdditionalFile({album, file}), }) } ${ album.commentary && fixWS` -

    ${language.$("releaseInfo.artistCommentary")}

    +

    ${language.$('releaseInfo.artistCommentary')}

    ${transformMultiline(album.commentary)}
    @@ -348,16 +348,16 @@ export function write(album, { wikiData }) { }), nav: { - linkContainerClasses: ["nav-links-hierarchy"], + linkContainerClasses: ['nav-links-hierarchy'], links: [ - { toHome: true }, + {toHome: true}, { - html: language.$("albumPage.nav.album", { - album: link.album(album, { class: "current" }), + html: language.$('albumPage.nav.album', { + album: link.album(album, {class: 'current'}), }), }, ], - bottomRowContent: generateAlbumNavLinks(album, null, { language }), + bottomRowContent: generateAlbumNavLinks(album, null, {language}), content: generateAlbumChronologyLinks(album, null, { generateChronologyLinks, }), @@ -380,14 +380,7 @@ export function write(album, { wikiData }) { export function generateAlbumSidebar( album, currentTrack, - { - fancifyURL, - getLinkThemeString, - link, - language, - transformMultiline, - wikiData, - } + {fancifyURL, getLinkThemeString, link, language, transformMultiline, wikiData} ) { const listTag = getAlbumListTag(album); @@ -400,41 +393,41 @@ export function generateAlbumSidebar( }]; */ - const { trackGroups } = album; + const {trackGroups} = album; const trackToListItem = (track) => html.tag( - "li", - { class: track === currentTrack && "current" }, - language.$("albumSidebar.trackList.item", { + 'li', + {class: track === currentTrack && 'current'}, + language.$('albumSidebar.trackList.item', { track: link.track(track), }) ); const nameOrDefault = (isDefaultTrackGroup, name) => isDefaultTrackGroup - ? language.$("albumSidebar.trackList.fallbackGroupName") + ? language.$('albumSidebar.trackList.fallbackGroupName') : name; const trackListPart = fixWS`

    ${link.album(album)}

    ${trackGroups - .map(({ name, color, startIndex, tracks, isDefaultTrackGroup }) => + .map(({name, color, startIndex, tracks, isDefaultTrackGroup}) => html.tag( - "details", + 'details', { // Leave side8ar track groups collapsed on al8um homepage, // since there's already a view of all the groups expanded // in the main content area. open: currentTrack && tracks.includes(currentTrack), - class: tracks.includes(currentTrack) && "current", + class: tracks.includes(currentTrack) && 'current', }, [ html.tag( - "summary", - { style: getLinkThemeString(color) }, - listTag === "ol" - ? language.$("albumSidebar.trackList.group.withRange", { + 'summary', + {style: getLinkThemeString(color)}, + listTag === 'ol' + ? language.$('albumSidebar.trackList.group.withRange', { group: `${nameOrDefault( isDefaultTrackGroup, name @@ -443,7 +436,7 @@ export function generateAlbumSidebar( startIndex + tracks.length }`, }) - : language.$("albumSidebar.trackList.group", { + : language.$('albumSidebar.trackList.group', { group: `${nameOrDefault( isDefaultTrackGroup, name @@ -452,20 +445,20 @@ export function generateAlbumSidebar( ), fixWS` <${ - listTag === "ol" + listTag === 'ol' ? `ol start="${startIndex + 1}"` : listTag }> - ${tracks.map(trackToListItem).join("\n")} + ${tracks.map(trackToListItem).join('\n')} `, ] ) ) - .join("\n")} + .join('\n')} `; - const { groups } = album; + const {groups} = album; const groupParts = groups .map((group) => { @@ -473,17 +466,17 @@ export function generateAlbumSidebar( const index = albums.indexOf(album); const next = index >= 0 && albums[index + 1]; const previous = index > 0 && albums[index - 1]; - return { group, next, previous }; + return {group, next, previous}; }) .map( - ({ group, next, previous }) => fixWS` -

    ${language.$("albumSidebar.groupBox.title", { + ({group, next, previous}) => fixWS` +

    ${language.$('albumSidebar.groupBox.title', { group: link.groupInfo(group), })}

    ${!currentTrack && transformMultiline(group.descriptionShort)} ${ group.urls?.length && - `

    ${language.$("releaseInfo.visitOn", { + `

    ${language.$('releaseInfo.visitOn', { links: language.formatDisjunctionList( group.urls.map((url) => fancifyURL(url)) ), @@ -495,7 +488,7 @@ export function generateAlbumSidebar( ${ next && `


    \n"); + const combinedGroupPart = groupParts.join('\n
    \n'); return { multiple: [trackListPart, combinedGroupPart], }; @@ -536,9 +529,9 @@ export function generateAlbumSidebar( export function generateAlbumSecondaryNav( album, currentTrack, - { link, language, getLinkThemeString } + {link, language, getLinkThemeString} ) { - const { groups } = album; + const {groups} = album; if (!groups.length) { return null; @@ -550,56 +543,56 @@ export function generateAlbumSecondaryNav( const index = albums.indexOf(album); const next = index >= 0 && albums[index + 1]; const previous = index > 0 && albums[index - 1]; - return { group, next, previous }; + return {group, next, previous}; }) - .map(({ group, next, previous }) => { + .map(({group, next, previous}) => { const previousNext = !currentTrack && [ previous && link.album(previous, { color: false, - text: language.$("misc.nav.previous"), + text: language.$('misc.nav.previous'), }), next && link.album(next, { color: false, - text: language.$("misc.nav.next"), + text: language.$('misc.nav.next'), }), ].filter(Boolean); - return html.tag("span", { style: getLinkThemeString(group.color) }, [ - language.$("albumSidebar.groupBox.title", { + return html.tag('span', {style: getLinkThemeString(group.color)}, [ + language.$('albumSidebar.groupBox.title', { group: link.groupInfo(group), }), - previousNext?.length && `(${previousNext.join(",\n")})`, + previousNext?.length && `(${previousNext.join(',\n')})`, ]); }); return { - classes: ["dot-between-spans"], - content: groupParts.join("\n"), + classes: ['dot-between-spans'], + content: groupParts.join('\n'), }; } export function generateAlbumNavLinks( album, currentTrack, - { generatePreviousNextLinks, language } + {generatePreviousNextLinks, language} ) { if (album.tracks.length <= 1) { - return ""; + return ''; } const previousNextLinks = currentTrack && generatePreviousNextLinks(currentTrack, { data: album.tracks, - linkKey: "track", + linkKey: 'track', }); const randomLink = `${ currentTrack - ? language.$("trackPage.nav.random") - : language.$("albumPage.nav.randomTrack") + ? language.$('trackPage.nav.random') + : language.$('albumPage.nav.randomTrack') }`; return previousNextLinks @@ -610,44 +603,44 @@ export function generateAlbumNavLinks( export function generateAlbumChronologyLinks( album, currentTrack, - { generateChronologyLinks } + {generateChronologyLinks} ) { return html.tag( - "div", + 'div', { [html.onlyIfContent]: true, - class: "nav-chronology-links", + class: 'nav-chronology-links', }, [ currentTrack && generateChronologyLinks(currentTrack, { - contribKey: "artistContribs", + contribKey: 'artistContribs', getThings: (artist) => [ ...artist.tracksAsArtist, ...artist.tracksAsContributor, ], - headingString: "misc.chronology.heading.track", + headingString: 'misc.chronology.heading.track', }), currentTrack && generateChronologyLinks(currentTrack, { - contribKey: "contributorContribs", + contribKey: 'contributorContribs', getThings: (artist) => [ ...artist.tracksAsArtist, ...artist.tracksAsContributor, ], - headingString: "misc.chronology.heading.track", + headingString: 'misc.chronology.heading.track', }), generateChronologyLinks(currentTrack || album, { - contribKey: "coverArtistContribs", - dateKey: "coverArtDate", + contribKey: 'coverArtistContribs', + dateKey: 'coverArtDate', getThings: (artist) => [ ...artist.albumsAsCoverArtist, ...artist.tracksAsCoverArtist, ], - headingString: "misc.chronology.heading.coverArt", + headingString: 'misc.chronology.heading.coverArt', }), ] .filter(Boolean) - .join("\n") + .join('\n') ); } diff --git a/src/page/artist-alias.js b/src/page/artist-alias.js index 8a45503..46ad1a3 100644 --- a/src/page/artist-alias.js +++ b/src/page/artist-alias.js @@ -1,21 +1,21 @@ -// @format -// +/** @format */ + // Artist alias redirect pages. // (Makes old permalinks bring visitors to the up-to-date page.) -export function targets({ wikiData }) { +export function targets({wikiData}) { return wikiData.artistAliasData; } -export function write(aliasArtist, { wikiData }) { +export function write(aliasArtist, {wikiData}) { // This function doesn't actually use wikiData, 8ut, um, consistency? - const { aliasedArtist } = aliasArtist; + const {aliasedArtist} = aliasArtist; const redirect = { - type: "redirect", - fromPath: ["artist", aliasArtist.directory], - toPath: ["artist", aliasedArtist.directory], + type: 'redirect', + fromPath: ['artist', aliasArtist.directory], + toPath: ['artist', aliasedArtist.directory], title: () => aliasedArtist.name, }; diff --git a/src/page/artist.js b/src/page/artist.js index 314d2aa..6b26b0f 100644 --- a/src/page/artist.js +++ b/src/page/artist.js @@ -1,16 +1,16 @@ -// @format -// +/** @format */ + // Artist page specification. // // NB: See artist-alias.js for artist alias redirect pages. // Imports -import fixWS from "fix-whitespace"; +import fixWS from 'fix-whitespace'; -import * as html from "../util/html.js"; +import * as html from '../util/html.js'; -import { bindOpts, unique } from "../util/sugar.js"; +import {bindOpts, unique} from '../util/sugar.js'; import { chunkByProperties, @@ -19,18 +19,18 @@ import { sortByDate, sortByDirectory, sortChronologically, -} from "../util/wiki-data.js"; +} from '../util/wiki-data.js'; // Page exports -export function targets({ wikiData }) { +export function targets({wikiData}) { return wikiData.artistData; } -export function write(artist, { wikiData }) { - const { groupData, wikiInfo } = wikiData; +export function write(artist, {wikiData}) { + const {groupData, wikiInfo} = wikiData; - const { name, urls, contextNotes } = artist; + const {name, urls, contextNotes} = artist; const artThingsAll = sortAlbumsTracksChronologically( unique([ @@ -39,7 +39,7 @@ export function write(artist, { wikiData }) { ...(artist.albumsAsBannerArtist ?? []), ...(artist.tracksAsCoverArtist ?? []), ]), - { getDate: (o) => o.coverArtDate } + {getDate: (o) => o.coverArtDate} ); const artThingsGallery = sortAlbumsTracksChronologically( @@ -47,7 +47,7 @@ export function write(artist, { wikiData }) { ...(artist.albumsAsCoverArtist ?? []), ...(artist.tracksAsCoverArtist ?? []), ], - { getDate: (o) => o.coverArtDate } + {getDate: (o) => o.coverArtDate} ); const commentaryThings = sortAlbumsTracksChronologically([ @@ -58,17 +58,17 @@ export function write(artist, { wikiData }) { const hasGallery = artThingsGallery.length > 0; const getArtistsAndContrib = (thing, key) => ({ - artists: thing[key]?.filter(({ who }) => who !== artist), - contrib: thing[key]?.find(({ who }) => who === artist), + artists: thing[key]?.filter(({who}) => who !== artist), + contrib: thing[key]?.find(({who}) => who === artist), thing, key, }); const artListChunks = chunkByProperties( artThingsAll.flatMap((thing) => - ["coverArtistContribs", "wallpaperArtistContribs", "bannerArtistContribs"] + ['coverArtistContribs', 'wallpaperArtistContribs', 'bannerArtistContribs'] .map((key) => getArtistsAndContrib(thing, key)) - .filter(({ contrib }) => contrib) + .filter(({contrib}) => contrib) .map((props) => ({ album: thing.album || thing, track: thing.album ? thing : null, @@ -76,7 +76,7 @@ export function write(artist, { wikiData }) { ...props, })) ), - ["date", "album"] + ['date', 'album'] ); const commentaryListChunks = chunkByProperties( @@ -84,7 +84,7 @@ export function write(artist, { wikiData }) { album: thing.album || thing, track: thing.album ? thing : null, })), - ["album"] + ['album'] ); const allTracks = sortAlbumsTracksChronologically( @@ -101,19 +101,19 @@ export function write(artist, { wikiData }) { date: +track.date, album: track.album, duration: track.duration, - artists: track.artistContribs.some(({ who }) => who === artist) - ? track.artistContribs.filter(({ who }) => who !== artist) - : track.contributorContribs.filter(({ who }) => who !== artist), + artists: track.artistContribs.some(({who}) => who === artist) + ? track.artistContribs.filter(({who}) => who !== artist) + : track.contributorContribs.filter(({who}) => who !== artist), contrib: { who: artist, whatArray: [ - track.artistContribs.find(({ who }) => who === artist)?.what, - track.contributorContribs.find(({ who }) => who === artist)?.what, + track.artistContribs.find(({who}) => who === artist)?.what, + track.contributorContribs.find(({who}) => who === artist)?.what, ].filter(Boolean), }, })), - ["date", "album"] - ).map(({ date, album, chunk }) => ({ + ['date', 'album'] + ).map(({date, album, chunk}) => ({ date, album, chunk, @@ -132,7 +132,7 @@ export function write(artist, { wikiData }) { group, contributions: usedGroups.filter((g) => g === group).length, })) - .filter(({ contributions }) => contributions > 0) + .filter(({contributions}) => contributions > 0) .sort((a, b) => b.contributions - a.contributions); }; @@ -151,10 +151,10 @@ export function write(artist, { wikiData }) { // want to show the full list of other contri8utors inline. // (It can often 8e very, very large!) artists: [], - contrib: flash.contributorContribs.find(({ who }) => who === artist), + contrib: flash.contributorContribs.find(({who}) => who === artist), })), - ["act"] - ).map(({ act, chunk }) => ({ + ['act'] + ).map(({act, chunk}) => ({ act, chunk, dateFirst: chunk[0].date, @@ -171,11 +171,11 @@ export function write(artist, { wikiData }) { contrib, }) => original - ? language.$("artistPage.creditList.entry.rerelease", { entry }) + ? language.$('artistPage.creditList.entry.rerelease', {entry}) : artists.length ? contrib.what || contrib.whatArray?.length ? language.$( - "artistPage.creditList.entry.withArtists.withContribution", + 'artistPage.creditList.entry.withArtists.withContribution', { entry, artists: getArtistString(artists), @@ -184,12 +184,12 @@ export function write(artist, { wikiData }) { : contrib.what, } ) - : language.$("artistPage.creditList.entry.withArtists", { + : language.$('artistPage.creditList.entry.withArtists', { entry, artists: getArtistString(artists), }) : contrib.what || contrib.whatArray?.length - ? language.$("artistPage.creditList.entry.withContribution", { + ? language.$('artistPage.creditList.entry.withContribution', { entry, contribution: contrib.whatArray ? language.formatUnitList(contrib.whatArray) @@ -199,16 +199,16 @@ export function write(artist, { wikiData }) { const unbound_generateTrackList = ( chunks, - { getArtistString, link, language } + {getArtistString, link, language} ) => fixWS`
    ${chunks .map( - ({ date, album, chunk, duration }) => fixWS` + ({date, album, chunk, duration}) => fixWS`
    ${ date && duration ? language.$( - "artistPage.creditList.album.withDate.withDuration", + 'artistPage.creditList.album.withDate.withDuration', { album: link.album(album), date: language.formatDate(date), @@ -218,27 +218,27 @@ export function write(artist, { wikiData }) { } ) : date - ? language.$("artistPage.creditList.album.withDate", { + ? language.$('artistPage.creditList.album.withDate', { album: link.album(album), date: language.formatDate(date), }) : duration - ? language.$("artistPage.creditList.album.withDuration", { + ? language.$('artistPage.creditList.album.withDuration', { album: link.album(album), duration: language.formatDuration(duration, { approximate: true, }), }) - : language.$("artistPage.creditList.album", { + : language.$('artistPage.creditList.album', { album: link.album(album), }) }
      ${chunk - .map(({ track, ...props }) => ({ + .map(({track, ...props}) => ({ original: track.originalReleaseTrack, entry: language.$( - "artistPage.creditList.entry.track.withDuration", + 'artistPage.creditList.entry.track.withDuration', { track: link.track(track), duration: language.formatDuration( @@ -248,10 +248,10 @@ export function write(artist, { wikiData }) { ), ...props, })) - .map(({ original, ...opts }) => + .map(({original, ...opts}) => html.tag( - "li", - { class: original && "rerelease" }, + 'li', + {class: original && 'rerelease'}, generateEntryAccents({ getArtistString, language, @@ -260,18 +260,18 @@ export function write(artist, { wikiData }) { }) ) ) - .join("\n")} + .join('\n')}
    ` ) - .join("\n")} + .join('\n')}
    `; const unbound_serializeArtistsAndContrib = - (key, { serializeContribs, serializeLink }) => + (key, {serializeContribs, serializeLink}) => (thing) => { - const { artists, contrib } = getArtistsAndContrib(thing, key); + const {artists, contrib} = getArtistsAndContrib(thing, key); const ret = {}; ret.link = serializeLink(thing); if (contrib.what) ret.contribution = contrib.what; @@ -279,21 +279,21 @@ export function write(artist, { wikiData }) { return ret; }; - const unbound_serializeTrackListChunks = (chunks, { serializeLink }) => - chunks.map(({ date, album, chunk, duration }) => ({ + const unbound_serializeTrackListChunks = (chunks, {serializeLink}) => + chunks.map(({date, album, chunk, duration}) => ({ album: serializeLink(album), date, duration, - tracks: chunk.map(({ track }) => ({ + tracks: chunk.map(({track}) => ({ link: serializeLink(track), duration: track.duration, })), })); const data = { - type: "data", - path: ["artist", artist.directory], - data: ({ serializeContribs, serializeLink }) => { + type: 'data', + path: ['artist', artist.directory], + data: ({serializeContribs, serializeLink}) => { const serializeArtistsAndContrib = bindOpts( unbound_serializeArtistsAndContrib, { @@ -312,22 +312,22 @@ export function write(artist, { wikiData }) { return { albums: { asCoverArtist: artist.albumsAsCoverArtist?.map( - serializeArtistsAndContrib("coverArtistContribs") + serializeArtistsAndContrib('coverArtistContribs') ), asWallpaperArtist: artist.albumsAsWallpaperArtist?.map( - serializeArtistsAndContrib("wallpaperArtistContribs") + serializeArtistsAndContrib('wallpaperArtistContribs') ), asBannerArtist: artist.albumsAsBannerArtist?.map( - serializeArtistsAndContrib("bannerArtistContribs") + serializeArtistsAndContrib('bannerArtistContribs') ), }, flashes: wikiInfo.enableFlashesAndGames ? { asContributor: artist.flashesAsContributor ?.map((flash) => - getArtistsAndContrib(flash, "contributorContribs") + getArtistsAndContrib(flash, 'contributorContribs') ) - .map(({ contrib, thing: flash }) => ({ + .map(({contrib, thing: flash}) => ({ link: serializeLink(flash), contribution: contrib.what, })), @@ -335,10 +335,10 @@ export function write(artist, { wikiData }) { : null, tracks: { asArtist: artist.tracksAsArtist.map( - serializeArtistsAndContrib("artistContribs") + serializeArtistsAndContrib('artistContribs') ), asContributor: artist.tracksAsContributor.map( - serializeArtistsAndContrib("contributorContribs") + serializeArtistsAndContrib('contributorContribs') ), chunked: serializeTrackListChunks(trackListChunks), }, @@ -347,8 +347,8 @@ export function write(artist, { wikiData }) { }; const infoPage = { - type: "page", - path: ["artist", artist.directory], + type: 'page', + path: ['artist', artist.directory], page: ({ fancifyURL, generateCoverLink, @@ -367,7 +367,7 @@ export function write(artist, { wikiData }) { }); return { - title: language.$("artistPage.title", { artist: name }), + title: language.$('artistPage.title', {artist: name}), main: { content: fixWS` @@ -375,16 +375,16 @@ export function write(artist, { wikiData }) { artist.hasAvatar && generateCoverLink({ src: getArtistAvatar(artist), - alt: language.$("misc.alt.artistAvatar"), + alt: language.$('misc.alt.artistAvatar'), }) } -

    ${language.$("artistPage.title", { +

    ${language.$('artistPage.title', { artist: name, })}

    ${ contextNotes && fixWS` -

    ${language.$("releaseInfo.note")}

    +

    ${language.$('releaseInfo.note')}

    ${transformMultiline(contextNotes)}
    @@ -393,41 +393,41 @@ export function write(artist, { wikiData }) { } ${ urls?.length && - `

    ${language.$("releaseInfo.visitOn", { + `

    ${language.$('releaseInfo.visitOn', { links: language.formatDisjunctionList( - urls.map((url) => fancifyURL(url, { language })) + urls.map((url) => fancifyURL(url, {language})) ), })}

    ` } ${ hasGallery && - `

    ${language.$("artistPage.viewArtGallery", { + `

    ${language.$('artistPage.viewArtGallery', { link: link.artistGallery(artist, { text: language.$( - "artistPage.viewArtGallery.link" + 'artistPage.viewArtGallery.link' ), }), })}

    ` } -

    ${language.$("misc.jumpTo.withLinks", { +

    ${language.$('misc.jumpTo.withLinks', { links: language.formatUnitList( [ allTracks.length && `${language.$( - "artistPage.trackList.title" + 'artistPage.trackList.title' )}`, artThingsAll.length && `${language.$( - "artistPage.artList.title" + 'artistPage.artList.title' )}`, wikiInfo.enableFlashesAndGames && flashes.length && `${language.$( - "artistPage.flashList.title" + 'artistPage.flashList.title' )}`, commentaryThings.length && `${language.$( - "artistPage.commentaryList.title" + 'artistPage.commentaryList.title' )}`, ].filter(Boolean) ), @@ -436,22 +436,22 @@ export function write(artist, { wikiData }) { allTracks.length && fixWS`

    ${language.$( - "artistPage.trackList.title" + 'artistPage.trackList.title' )}

    ${language.$( - "artistPage.contributedDurationLine", + 'artistPage.contributedDurationLine', { artist: artist.name, duration: language.formatDuration( totalDuration, - { approximate: true, unit: true } + {approximate: true, unit: true} ), } )}

    -

    ${language.$("artistPage.musicGroupsLine", { +

    ${language.$('artistPage.musicGroupsLine', { groups: language.formatUnitList( - musicGroups.map(({ group, contributions }) => - language.$("artistPage.groupsLine.item", { + musicGroups.map(({group, contributions}) => + language.$('artistPage.groupsLine.item', { group: link.groupInfo(group), contributions: language.countContributions( @@ -468,25 +468,25 @@ export function write(artist, { wikiData }) { artThingsAll.length && fixWS`

    ${language.$( - "artistPage.artList.title" + 'artistPage.artList.title' )}

    ${ hasGallery && `

    ${language.$( - "artistPage.viewArtGallery.orBrowseList", + 'artistPage.viewArtGallery.orBrowseList', { link: link.artistGallery(artist, { text: language.$( - "artistPage.viewArtGallery.link" + 'artistPage.viewArtGallery.link' ), }), } )}

    ` } -

    ${language.$("artistPage.artGroupsLine", { +

    ${language.$('artistPage.artGroupsLine', { groups: language.formatUnitList( - artGroups.map(({ group, contributions }) => - language.$("artistPage.groupsLine.item", { + artGroups.map(({group, contributions}) => + language.$('artistPage.groupsLine.item', { group: link.groupInfo(group), contributions: language.countContributions( @@ -499,9 +499,9 @@ export function write(artist, { wikiData }) {

    ${artListChunks .map( - ({ date, album, chunk }) => fixWS` + ({date, album, chunk}) => fixWS`
    ${language.$( - "artistPage.creditList.album.withDate", + 'artistPage.creditList.album.withDate', { album: link.album(album), date: language.formatDate(date), @@ -518,20 +518,20 @@ export function write(artist, { wikiData }) { }) => ({ entry: track ? language.$( - "artistPage.creditList.entry.track", + 'artistPage.creditList.entry.track', { track: link.track(track), } ) : `${language.$( - "artistPage.creditList.entry.album." + + 'artistPage.creditList.entry.album.' + { wallpaperArtistContribs: - "wallpaperArt", + 'wallpaperArt', bannerArtistContribs: - "bannerArt", + 'bannerArt', coverArtistContribs: - "coverArt", + 'coverArt', }[key] )}`, ...props, @@ -545,11 +545,11 @@ export function write(artist, { wikiData }) { }) ) .map((row) => `
  • ${row}
  • `) - .join("\n")} + .join('\n')} ` ) - .join("\n")} + .join('\n')}
    ` } @@ -558,7 +558,7 @@ export function write(artist, { wikiData }) { flashes.length && fixWS`

    ${language.$( - "artistPage.flashList.title" + 'artistPage.flashList.title' )}

    ${flashListChunks @@ -570,7 +570,7 @@ export function write(artist, { wikiData }) { dateLast, }) => fixWS`
    ${language.$( - "artistPage.creditList.flashAct.withDateRange", + 'artistPage.creditList.flashAct.withDateRange', { act: link.flash(chunk[0].flash, { text: act.name, @@ -583,9 +583,9 @@ export function write(artist, { wikiData }) { )}
      ${chunk - .map(({ flash, ...props }) => ({ + .map(({flash, ...props}) => ({ entry: language.$( - "artistPage.creditList.entry.flash", + 'artistPage.creditList.entry.flash', { flash: link.flash(flash), } @@ -600,11 +600,11 @@ export function write(artist, { wikiData }) { }) ) .map((row) => `
    • ${row}
    • `) - .join("\n")} + .join('\n')}
    ` ) - .join("\n")} + .join('\n')}
    ` } @@ -612,38 +612,38 @@ export function write(artist, { wikiData }) { commentaryThings.length && fixWS`

    ${language.$( - "artistPage.commentaryList.title" + 'artistPage.commentaryList.title' )}

    ${commentaryListChunks .map( - ({ album, chunk }) => fixWS` + ({album, chunk}) => fixWS`
    ${language.$( - "artistPage.creditList.album", + 'artistPage.creditList.album', { album: link.album(album), } )}
      ${chunk - .map(({ album, track, ...props }) => + .map(({album, track, ...props}) => track ? language.$( - "artistPage.creditList.entry.track", + 'artistPage.creditList.entry.track', { track: link.track(track), } ) : `${language.$( - "artistPage.creditList.entry.album.commentary" + 'artistPage.creditList.entry.album.commentary' )}` ) .map((row) => `
    • ${row}
    • `) - .join("\n")} + .join('\n')}
    ` ) - .join("\n")} + .join('\n')}
    ` } @@ -661,8 +661,8 @@ export function write(artist, { wikiData }) { }; const galleryPage = hasGallery && { - type: "page", - path: ["artistGallery", artist.directory], + type: 'page', + path: ['artistGallery', artist.directory], page: ({ generateInfoGalleryLinks, getAlbumCover, @@ -672,26 +672,26 @@ export function write(artist, { wikiData }) { language, to, }) => ({ - title: language.$("artistGalleryPage.title", { artist: name }), + title: language.$('artistGalleryPage.title', {artist: name}), main: { - classes: ["top-index"], + classes: ['top-index'], content: fixWS` -

    ${language.$("artistGalleryPage.title", { +

    ${language.$('artistGalleryPage.title', { artist: name, })}

    ${language.$( - "artistGalleryPage.infoLine", + 'artistGalleryPage.infoLine', { coverArts: language.countCoverArts( artThingsGallery.length, - { unit: true } + {unit: true} ), } )}

    ${getGridHTML({ - entries: artThingsGallery.map((item) => ({ item })), + entries: artThingsGallery.map((item) => ({item})), srcFn: (thing) => thing.album ? getTrackCover(thing) @@ -723,30 +723,30 @@ function generateNavForArtist( artist, isGallery, hasGallery, - { generateInfoGalleryLinks, link, language, wikiData } + {generateInfoGalleryLinks, link, language, wikiData} ) { - const { wikiInfo } = wikiData; + const {wikiInfo} = wikiData; const infoGalleryLinks = hasGallery && generateInfoGalleryLinks(artist, isGallery, { link, language, - linkKeyGallery: "artistGallery", - linkKeyInfo: "artist", + linkKeyGallery: 'artistGallery', + linkKeyInfo: 'artist', }); return { - linkContainerClasses: ["nav-links-hierarchy"], + linkContainerClasses: ['nav-links-hierarchy'], links: [ - { toHome: true }, + {toHome: true}, wikiInfo.enableListings && { - path: ["localized.listingIndex"], - title: language.$("listingIndex.title"), + path: ['localized.listingIndex'], + title: language.$('listingIndex.title'), }, { - html: language.$("artistPage.nav.artist", { - artist: link.artist(artist, { class: "current" }), + html: language.$('artistPage.nav.artist', { + artist: link.artist(artist, {class: 'current'}), }), }, hasGallery && { diff --git a/src/page/flash.js b/src/page/flash.js index 864f44d..340927d 100644 --- a/src/page/flash.js +++ b/src/page/flash.js @@ -1,29 +1,29 @@ -// @format -// +/** @format */ + // Flash page and index specifications. // Imports -import fixWS from "fix-whitespace"; +import fixWS from 'fix-whitespace'; -import * as html from "../util/html.js"; +import * as html from '../util/html.js'; -import { getFlashLink } from "../util/wiki-data.js"; +import {getFlashLink} from '../util/wiki-data.js'; // Page exports -export function condition({ wikiData }) { +export function condition({wikiData}) { return wikiData.wikiInfo.enableFlashesAndGames; } -export function targets({ wikiData }) { +export function targets({wikiData}) { return wikiData.flashData; } -export function write(flash, { wikiData }) { +export function write(flash, {wikiData}) { const page = { - type: "page", - path: ["flash", flash.directory], + type: 'page', + path: ['flash', flash.directory], page: ({ fancifyFlashURL, generateChronologyLinks, @@ -36,26 +36,26 @@ export function write(flash, { wikiData }) { language, transformInline, }) => ({ - title: language.$("flashPage.title", { flash: flash.name }), + title: language.$('flashPage.title', {flash: flash.name}), theme: getThemeString(flash.color, [ `--flash-directory: ${flash.directory}`, ]), main: { content: fixWS` -

    ${language.$("flashPage.title", { +

    ${language.$('flashPage.title', { flash: flash.name, })}

    ${generateCoverLink({ src: getFlashCover(flash), - alt: language.$("misc.alt.flashArt"), + alt: language.$('misc.alt.flashArt'), })} -

    ${language.$("releaseInfo.released", { +

    ${language.$('releaseInfo.released', { date: language.formatDate(flash.date), })}

    ${ (flash.page || flash.urls?.length) && - `

    ${language.$("releaseInfo.playOn", { + `

    ${language.$('releaseInfo.playOn', { links: language.formatDisjunctionList( [ flash.page && getFlashLink(flash), @@ -69,15 +69,15 @@ export function write(flash, { wikiData }) { fixWS`

    Tracks featured in ${flash.name.replace( /\.$/, - "" + '' )}:

      ${flash.featuredTracks .map((track) => - language.$("trackList.item.withArtists", { + language.$('trackList.item.withArtists', { track: link.track(track), by: `${language.$( - "trackList.item.withArtists.by", + 'trackList.item.withArtists.by', { artists: getArtistString( track.artistContribs @@ -87,14 +87,14 @@ export function write(flash, { wikiData }) { }) ) .map((row) => `
    • ${row}
    • `) - .join("\n")} + .join('\n')}
    ` } ${ flash.contributorContribs.length && fixWS` -

    ${language.$("releaseInfo.contributors")}

    +

    ${language.$('releaseInfo.contributors')}

      ${flash.contributorContribs .map( @@ -104,14 +104,14 @@ export function write(flash, { wikiData }) { showIcons: true, })}` ) - .join("\n")} + .join('\n')}
    ` } `, }, - sidebarLeft: generateSidebarForFlash(flash, { link, language, wikiData }), + sidebarLeft: generateSidebarForFlash(flash, {link, language, wikiData}), nav: generateNavForFlash(flash, { generateChronologyLinks, generatePreviousNextLinks, @@ -125,32 +125,32 @@ export function write(flash, { wikiData }) { return [page]; } -export function writeTargetless({ wikiData }) { - const { flashActData } = wikiData; +export function writeTargetless({wikiData}) { + const {flashActData} = wikiData; const page = { - type: "page", - path: ["flashIndex"], - page: ({ getFlashGridHTML, getLinkThemeString, link, language }) => ({ - title: language.$("flashIndex.title"), + type: 'page', + path: ['flashIndex'], + page: ({getFlashGridHTML, getLinkThemeString, link, language}) => ({ + title: language.$('flashIndex.title'), main: { - classes: ["flash-index"], + classes: ['flash-index'], content: fixWS` -

    ${language.$("flashIndex.title")}

    +

    ${language.$('flashIndex.title')}

    -

    ${language.$("misc.jumpTo")}

    +

    ${language.$('misc.jumpTo')}

      ${flashActData .filter((act) => act.jump) .map( - ({ anchor, jump, jumpColor }) => fixWS` + ({anchor, jump, jumpColor}) => fixWS`
    • ${jump}
    • ` ) - .join("\n")} + .join('\n')}
    ${flashActData @@ -171,11 +171,11 @@ export function writeTargetless({ wikiData }) {
    ` ) - .join("\n")} + .join('\n')} `, }, - nav: { simple: true }, + nav: {simple: true}, }), }; @@ -186,32 +186,26 @@ export function writeTargetless({ wikiData }) { function generateNavForFlash( flash, - { - generateChronologyLinks, - generatePreviousNextLinks, - link, - language, - wikiData, - } + {generateChronologyLinks, generatePreviousNextLinks, link, language, wikiData} ) { - const { flashData, wikiInfo } = wikiData; + const {flashData, wikiInfo} = wikiData; const previousNextLinks = generatePreviousNextLinks(flash, { data: flashData, - linkKey: "flash", + linkKey: 'flash', }); return { - linkContainerClasses: ["nav-links-hierarchy"], + linkContainerClasses: ['nav-links-hierarchy'], links: [ - { toHome: true }, + {toHome: true}, { - path: ["localized.flashIndex"], - title: language.$("flashIndex.title"), + path: ['localized.flashIndex'], + title: language.$('flashIndex.title'), }, { - html: language.$("flashPage.nav.flash", { - flash: link.flash(flash, { class: "current" }), + html: language.$('flashPage.nav.flash', { + flash: link.flash(flash, {class: 'current'}), }), }, ], @@ -221,8 +215,8 @@ function generateNavForFlash( content: fixWS`
    ${generateChronologyLinks(flash, { - headingString: "misc.chronology.heading.flash", - contribKey: "contributorContribs", + headingString: 'misc.chronology.heading.flash', + contribKey: 'contributorContribs', getThings: (artist) => artist.flashesAsContributor, })}
    @@ -230,21 +224,21 @@ function generateNavForFlash( }; } -function generateSidebarForFlash(flash, { link, language, wikiData }) { +function generateSidebarForFlash(flash, {link, language, wikiData}) { // all hard-coded, sorry :( // this doesnt have a super portable implementation/design...yet!! - const { flashActData } = wikiData; + const {flashActData} = wikiData; - const act6 = flashActData.findIndex((act) => act.name.startsWith("Act 6")); + const act6 = flashActData.findIndex((act) => act.name.startsWith('Act 6')); const postCanon = flashActData.findIndex((act) => - act.name.includes("Post Canon") + act.name.includes('Post Canon') ); const outsideCanon = postCanon + flashActData .slice(postCanon) - .findIndex((act) => !act.name.includes("Post Canon")); + .findIndex((act) => !act.name.includes('Post Canon')); const actIndex = flashActData.indexOf(flash.act); const side = actIndex < 0 ? 0 : actIndex < act6 ? 1 : actIndex <= outsideCanon ? 2 : 3; @@ -252,18 +246,18 @@ function generateSidebarForFlash(flash, { link, language, wikiData }) { return { content: fixWS` -

    ${link.flashIndex("", { - text: language.$("flashIndex.title"), +

    ${link.flashIndex('', { + text: language.$('flashIndex.title'), })}

    ${flashActData .filter( (act) => - act.name.startsWith("Act 1") || - act.name.startsWith("Act 6 Act 1") || - act.name.startsWith("Hiveswap") || + act.name.startsWith('Act 1') || + act.name.startsWith('Act 6 Act 1') || + act.name.startsWith('Hiveswap') || // Sorry not sorry -Yiffy - (({ index = flashActData.indexOf(act) } = {}) => + (({index = flashActData.indexOf(act)} = {}) => index < act6 ? side === 1 : index < outsideCanon @@ -271,43 +265,43 @@ function generateSidebarForFlash(flash, { link, language, wikiData }) { : true)() ) .flatMap((act) => [ - (act.name.startsWith("Act 1") && + (act.name.startsWith('Act 1') && html.tag( - "dt", - { class: ["side", side === 1 && "current"] }, + 'dt', + {class: ['side', side === 1 && 'current']}, link.flash(act.flashes[0], { - color: "#4ac925", + color: '#4ac925', text: `Side 1 (Acts 1-5)`, }) )) || - (act.name.startsWith("Act 6 Act 1") && + (act.name.startsWith('Act 6 Act 1') && html.tag( - "dt", - { class: ["side", side === 2 && "current"] }, + 'dt', + {class: ['side', side === 2 && 'current']}, link.flash(act.flashes[0], { - color: "#1076a2", + color: '#1076a2', text: `Side 2 (Acts 6-7)`, }) )) || - (act.name.startsWith("Hiveswap Act 1") && + (act.name.startsWith('Hiveswap Act 1') && html.tag( - "dt", - { class: ["side", side === 3 && "current"] }, + 'dt', + {class: ['side', side === 3 && 'current']}, link.flash(act.flashes[0], { - color: "#008282", + color: '#008282', text: `Outside Canon (Misc. Games)`, }) )), - (({ index = flashActData.indexOf(act) } = {}) => + (({index = flashActData.indexOf(act)} = {}) => index < act6 ? side === 1 : index < outsideCanon ? side === 2 : true)() && html.tag( - "dt", - { class: act === currentAct && "current" }, - link.flash(act.flashes[0], { text: act.name }) + 'dt', + {class: act === currentAct && 'current'}, + link.flash(act.flashes[0], {text: act.name}) ), act === currentAct && fixWS` @@ -315,17 +309,17 @@ function generateSidebarForFlash(flash, { link, language, wikiData }) { ${act.flashes .map((f) => html.tag( - "li", - { class: f === flash && "current" }, + 'li', + {class: f === flash && 'current'}, link.flash(f) ) ) - .join("\n")} + .join('\n')} `, ]) .filter(Boolean) - .join("\n")} + .join('\n')}
    `, }; diff --git a/src/page/group.js b/src/page/group.js index b6f9978..c7de2fb 100644 --- a/src/page/group.js +++ b/src/page/group.js @@ -1,25 +1,25 @@ -// @format -// +/** @format */ + // Group page specifications. // Imports -import fixWS from "fix-whitespace"; +import fixWS from 'fix-whitespace'; -import * as html from "../util/html.js"; +import * as html from '../util/html.js'; -import { getTotalDuration, sortChronologically } from "../util/wiki-data.js"; +import {getTotalDuration, sortChronologically} from '../util/wiki-data.js'; // Page exports -export function targets({ wikiData }) { +export function targets({wikiData}) { return wikiData.groupData; } -export function write(group, { wikiData }) { - const { listingSpec, wikiInfo } = wikiData; +export function write(group, {wikiData}) { + const {listingSpec, wikiInfo} = wikiData; - const { albums } = group; + const {albums} = group; const tracks = albums.flatMap((album) => album.tracks); const totalDuration = getTotalDuration(tracks); @@ -29,8 +29,8 @@ export function write(group, { wikiData }) { })); const infoPage = { - type: "page", - path: ["groupInfo", group.directory], + type: 'page', + path: ['groupInfo', group.directory], page: ({ generateInfoGalleryLinks, generatePreviousNextLinks, @@ -41,57 +41,57 @@ export function write(group, { wikiData }) { language, transformMultiline, }) => ({ - title: language.$("groupInfoPage.title", { group: group.name }), + title: language.$('groupInfoPage.title', {group: group.name}), theme: getThemeString(group.color), main: { content: fixWS` -

    ${language.$("groupInfoPage.title", { +

    ${language.$('groupInfoPage.title', { group: group.name, })}

    ${ group.urls?.length && - `

    ${language.$("releaseInfo.visitOn", { + `

    ${language.$('releaseInfo.visitOn', { links: language.formatDisjunctionList( - group.urls.map((url) => fancifyURL(url, { language })) + group.urls.map((url) => fancifyURL(url, {language})) ), })}

    ` }
    ${transformMultiline(group.description)}
    -

    ${language.$("groupInfoPage.albumList.title")}

    -

    ${language.$("groupInfoPage.viewAlbumGallery", { +

    ${language.$('groupInfoPage.albumList.title')}

    +

    ${language.$('groupInfoPage.viewAlbumGallery', { link: link.groupGallery(group, { - text: language.$("groupInfoPage.viewAlbumGallery.link"), + text: language.$('groupInfoPage.viewAlbumGallery.link'), }), })}

      ${albumLines - .map(({ album, otherGroup }) => { + .map(({album, otherGroup}) => { const item = album.date - ? language.$("groupInfoPage.albumList.item", { + ? language.$('groupInfoPage.albumList.item', { year: album.date.getFullYear(), album: link.album(album), }) : language.$( - "groupInfoPage.albumList.item.withoutYear", + 'groupInfoPage.albumList.item.withoutYear', { album: link.album(album), } ); return html.tag( - "li", + 'li', otherGroup ? language.$( - "groupInfoPage.albumList.item.withAccent", + 'groupInfoPage.albumList.item.withAccent', { item, accent: html.tag( - "span", - { class: "other-group-accent" }, + 'span', + {class: 'other-group-accent'}, language.$( - "groupInfoPage.albumList.item.otherGroupAccent", + 'groupInfoPage.albumList.item.otherGroupAccent', { group: link.groupInfo(otherGroup, { color: false, @@ -104,7 +104,7 @@ export function write(group, { wikiData }) { : item ); }) - .join("\n")} + .join('\n')}
    `, }, @@ -127,8 +127,8 @@ export function write(group, { wikiData }) { }; const galleryPage = { - type: "page", - path: ["groupGallery", group.directory], + type: 'page', + path: ['groupGallery', group.directory], page: ({ generateInfoGalleryLinks, generatePreviousNextLinks, @@ -138,17 +138,17 @@ export function write(group, { wikiData }) { link, language, }) => ({ - title: language.$("groupGalleryPage.title", { group: group.name }), + title: language.$('groupGalleryPage.title', {group: group.name}), theme: getThemeString(group.color), main: { - classes: ["top-index"], + classes: ['top-index'], content: fixWS` -

    ${language.$("groupGalleryPage.title", { +

    ${language.$('groupGalleryPage.title', { group: group.name, })}

    ${language.$( - "groupGalleryPage.infoLine", + 'groupGalleryPage.infoLine', { tracks: `${language.countTracks(tracks.length, { unit: true, @@ -165,16 +165,16 @@ export function write(group, { wikiData }) { wikiInfo.enableGroupUI && wikiInfo.enableListings && html.tag( - "p", - { class: "quick-info" }, - language.$("groupGalleryPage.anotherGroupLine", { + 'p', + {class: 'quick-info'}, + language.$('groupGalleryPage.anotherGroupLine', { link: link.listing( listingSpec.find( - (l) => l.directory === "groups/by-category" + (l) => l.directory === 'groups/by-category' ), { text: language.$( - "groupGalleryPage.anotherGroupLine.link" + 'groupGalleryPage.anotherGroupLine.link' ), } ), @@ -222,45 +222,45 @@ export function write(group, { wikiData }) { function generateGroupSidebar( currentGroup, isGallery, - { getLinkThemeString, link, language, wikiData } + {getLinkThemeString, link, language, wikiData} ) { - const { groupCategoryData, wikiInfo } = wikiData; + const {groupCategoryData, wikiInfo} = wikiData; if (!wikiInfo.enableGroupUI) { return null; } - const linkKey = isGallery ? "groupGallery" : "groupInfo"; + const linkKey = isGallery ? 'groupGallery' : 'groupInfo'; return { content: fixWS` -

    ${language.$("groupSidebar.title")}

    +

    ${language.$('groupSidebar.title')}

    ${groupCategoryData .map((category) => html.tag( - "details", + 'details', { open: category === currentGroup.category, - class: category === currentGroup.category && "current", + class: category === currentGroup.category && 'current', }, [ html.tag( - "summary", - { style: getLinkThemeString(category.color) }, - language.$("groupSidebar.groupList.category", { + 'summary', + {style: getLinkThemeString(category.color)}, + language.$('groupSidebar.groupList.category', { category: `${category.name}`, }) ), html.tag( - "ul", + 'ul', category.groups.map((group) => html.tag( - "li", + 'li', { - class: group === currentGroup && "current", + class: group === currentGroup && 'current', style: getLinkThemeString(group.color), }, - language.$("groupSidebar.groupList.item", { + language.$('groupSidebar.groupList.item', { group: link[linkKey](group), }) ) @@ -269,7 +269,7 @@ function generateGroupSidebar( ] ) ) - .join("\n")} + .join('\n')} `, }; @@ -286,18 +286,18 @@ function generateGroupNav( wikiData, } ) { - const { groupData, wikiInfo } = wikiData; + const {groupData, wikiInfo} = wikiData; if (!wikiInfo.enableGroupUI) { - return { simple: true }; + return {simple: true}; } - const urlKey = isGallery ? "localized.groupGallery" : "localized.groupInfo"; - const linkKey = isGallery ? "groupGallery" : "groupInfo"; + const urlKey = isGallery ? 'localized.groupGallery' : 'localized.groupInfo'; + const linkKey = isGallery ? 'groupGallery' : 'groupInfo'; const infoGalleryLinks = generateInfoGalleryLinks(currentGroup, isGallery, { - linkKeyGallery: "groupGallery", - linkKeyInfo: "groupInfo", + linkKeyGallery: 'groupGallery', + linkKeyInfo: 'groupInfo', }); const previousNextLinks = generatePreviousNextLinks(currentGroup, { @@ -306,16 +306,16 @@ function generateGroupNav( }); return { - linkContainerClasses: ["nav-links-hierarchy"], + linkContainerClasses: ['nav-links-hierarchy'], links: [ - { toHome: true }, + {toHome: true}, wikiInfo.enableListings && { - path: ["localized.listingIndex"], - title: language.$("listingIndex.title"), + path: ['localized.listingIndex'], + title: language.$('listingIndex.title'), }, { - html: language.$("groupPage.nav.group", { - group: link[linkKey](currentGroup, { class: "current" }), + html: language.$('groupPage.nav.group', { + group: link[linkKey](currentGroup, {class: 'current'}), }), }, { diff --git a/src/page/homepage.js b/src/page/homepage.js index 8d20acc..7701a73 100644 --- a/src/page/homepage.js +++ b/src/page/homepage.js @@ -1,23 +1,23 @@ -// @format -// +/** @format */ + // Homepage specification. // Imports -import fixWS from "fix-whitespace"; +import fixWS from 'fix-whitespace'; -import * as html from "../util/html.js"; +import * as html from '../util/html.js'; -import { getNewAdditions, getNewReleases } from "../util/wiki-data.js"; +import {getNewAdditions, getNewReleases} from '../util/wiki-data.js'; // Page exports -export function writeTargetless({ wikiData }) { - const { newsData, staticPageData, homepageLayout, wikiInfo } = wikiData; +export function writeTargetless({wikiData}) { + const {newsData, staticPageData, homepageLayout, wikiInfo} = wikiData; const page = { - type: "page", - path: ["home"], + type: 'page', + path: ['home'], page: ({ getAlbumGridHTML, getLinkThemeString, @@ -35,7 +35,7 @@ export function writeTargetless({ wikiData }) { }, main: { - classes: ["top-index"], + classes: ['top-index'], content: fixWS`

    ${wikiInfo.name}

    ${homepageLayout.rows @@ -46,21 +46,21 @@ export function writeTargetless({ wikiData }) { )}">

    ${row.name}

    ${ - row.type === "albums" && + row.type === 'albums' && fixWS`
    ${getAlbumGridHTML({ entries: (row.sourceGroupByRef === - "new-releases" + 'new-releases' ? getNewReleases( row.countAlbumsFromGroup, - { wikiData } + {wikiData} ) : row.sourceGroupByRef === - "new-additions" + 'new-additions' ? getNewAdditions( row.countAlbumsFromGroup, - { wikiData } + {wikiData} ) : (row.sourceGroup?.albums ?? []) .slice() @@ -70,7 +70,7 @@ export function writeTargetless({ wikiData }) { album.isListedOnHomepage ) .slice(0, row.countAlbumsFromGroup) - .map((album) => ({ item: album })) + .map((album) => ({item: album})) ).concat( row.sourceAlbums.map((album) => ({ item: album, @@ -85,11 +85,11 @@ export function writeTargetless({ wikiData }) { ${row.actionLinks .map((action) => transformInline(action).replace( - " ` } @@ -99,7 +99,7 @@ export function writeTargetless({ wikiData }) { ` ) - .join("\n")} + .join('\n')} `, }, @@ -117,21 +117,21 @@ export function writeTargetless({ wikiData }) { // And no, I will not make [[news]] into part of transformMultiline // (even though that would 8e hilarious). content: transformMultiline( - homepageLayout.sidebarContent.replace("[[news]]", "__GENERATE_NEWS__") + homepageLayout.sidebarContent.replace('[[news]]', '__GENERATE_NEWS__') ).replace( - "

    __GENERATE_NEWS__

    ", + '

    __GENERATE_NEWS__

    ', wikiInfo.enableNews ? fixWS` -

    ${language.$("homepage.news.title")}

    +

    ${language.$('homepage.news.title')}

    ${newsData .slice(0, 3) .map((entry, i) => html.tag( - "article", + 'article', { class: [ - "news-entry", - i === 0 && "first-news-entry", + 'news-entry', + i === 0 && 'first-news-entry', ], }, fixWS` @@ -143,42 +143,42 @@ export function writeTargetless({ wikiData }) { entry.contentShort !== entry.content && link.newsEntry(entry, { text: language.$( - "homepage.news.entry.viewRest" + 'homepage.news.entry.viewRest' ), }) } ` ) ) - .join("\n")} + .join('\n')} ` : `

    News requested in content description but this feature isn't enabled

    ` ), }, nav: { - linkContainerClasses: ["nav-links-index"], + linkContainerClasses: ['nav-links-index'], links: [ - link.home("", { text: wikiInfo.nameShort, class: "current", to }), + link.home('', {text: wikiInfo.nameShort, class: 'current', to}), wikiInfo.enableListings && - link.listingIndex("", { - text: language.$("listingIndex.title"), + link.listingIndex('', { + text: language.$('listingIndex.title'), to, }), wikiInfo.enableNews && - link.newsIndex("", { text: language.$("newsIndex.title"), to }), + link.newsIndex('', {text: language.$('newsIndex.title'), to}), wikiInfo.enableFlashesAndGames && - link.flashIndex("", { text: language.$("flashIndex.title"), to }), + link.flashIndex('', {text: language.$('flashIndex.title'), to}), ...staticPageData .filter((page) => page.showInNavigationBar) - .map((page) => link.staticPage(page, { text: page.nameShort })), + .map((page) => link.staticPage(page, {text: page.nameShort})), ] .filter(Boolean) - .map((html) => ({ html })), + .map((html) => ({html})), }, }), }; diff --git a/src/page/index.js b/src/page/index.js index 3d7e6fa..149503f 100644 --- a/src/page/index.js +++ b/src/page/index.js @@ -1,5 +1,5 @@ -// @format -// +/** @format */ + // NB: This is the index for the page/ directory and contains exports for all // other modules here! It's not the page spec for the homepage - see // homepage.js for that. @@ -41,15 +41,15 @@ // These functions should be referenced only from adjacent modules, as they // pertain only to site page generation. -export * as album from "./album.js"; -export * as albumCommentary from "./album-commentary.js"; -export * as artist from "./artist.js"; -export * as artistAlias from "./artist-alias.js"; -export * as flash from "./flash.js"; -export * as group from "./group.js"; -export * as homepage from "./homepage.js"; -export * as listing from "./listing.js"; -export * as news from "./news.js"; -export * as static from "./static.js"; -export * as tag from "./tag.js"; -export * as track from "./track.js"; +export * as album from './album.js'; +export * as albumCommentary from './album-commentary.js'; +export * as artist from './artist.js'; +export * as artistAlias from './artist-alias.js'; +export * as flash from './flash.js'; +export * as group from './group.js'; +export * as homepage from './homepage.js'; +export * as listing from './listing.js'; +export * as news from './news.js'; +export * as static from './static.js'; +export * as tag from './tag.js'; +export * as track from './track.js'; diff --git a/src/page/listing.js b/src/page/listing.js index 69fa491..90415de 100644 --- a/src/page/listing.js +++ b/src/page/listing.js @@ -1,5 +1,5 @@ -// @format -// +/** @format */ + // Listing page specification. // // The targets here are a bit different than for most pages: rather than data @@ -12,36 +12,36 @@ // Imports -import fixWS from "fix-whitespace"; +import fixWS from 'fix-whitespace'; -import * as html from "../util/html.js"; +import * as html from '../util/html.js'; -import { getTotalDuration } from "../util/wiki-data.js"; +import {getTotalDuration} from '../util/wiki-data.js'; // Page exports -export function condition({ wikiData }) { +export function condition({wikiData}) { return wikiData.wikiInfo.enableListings; } -export function targets({ wikiData }) { +export function targets({wikiData}) { return wikiData.listingSpec; } -export function write(listing, { wikiData }) { - if (listing.condition && !listing.condition({ wikiData })) { +export function write(listing, {wikiData}) { + if (listing.condition && !listing.condition({wikiData})) { return null; } - const { wikiInfo } = wikiData; + const {wikiInfo} = wikiData; - const data = listing.data ? listing.data({ wikiData }) : null; + const data = listing.data ? listing.data({wikiData}) : null; const page = { - type: "page", - path: ["listing", listing.directory], + type: 'page', + path: ['listing', listing.directory], page: (opts) => { - const { getLinkThemeString, link, language } = opts; + const {getLinkThemeString, link, language} = opts; const titleKey = `listingPage.${listing.stringsKey}.title`; return { @@ -63,7 +63,7 @@ export function write(listing, { wikiData }) { ${data .map((item) => listing.row(item, opts)) .map((row) => `
  • ${row}
  • `) - .join("\n")} + .join('\n')} ` } @@ -80,14 +80,14 @@ export function write(listing, { wikiData }) { }, nav: { - linkContainerClasses: ["nav-links-hierarchy"], + linkContainerClasses: ['nav-links-hierarchy'], links: [ - { toHome: true }, + {toHome: true}, { - path: ["localized.listingIndex"], - title: language.$("listingIndex.title"), + path: ['localized.listingIndex'], + title: language.$('listingIndex.title'), }, - { toCurrentPage: true }, + {toCurrentPage: true}, ], }, }; @@ -97,21 +97,21 @@ export function write(listing, { wikiData }) { return [page]; } -export function writeTargetless({ wikiData }) { - const { albumData, trackData, wikiInfo } = wikiData; +export function writeTargetless({wikiData}) { + const {albumData, trackData, wikiInfo} = wikiData; const totalDuration = getTotalDuration(trackData); const page = { - type: "page", - path: ["listingIndex"], - page: ({ getLinkThemeString, language, link }) => ({ - title: language.$("listingIndex.title"), + type: 'page', + path: ['listingIndex'], + page: ({getLinkThemeString, language, link}) => ({ + title: language.$('listingIndex.title'), main: { content: fixWS` -

    ${language.$("listingIndex.title")}

    -

    ${language.$("listingIndex.infoLine", { +

    ${language.$('listingIndex.title')}

    +

    ${language.$('listingIndex.infoLine', { wiki: wikiInfo.name, tracks: `${language.countTracks(trackData.length, { unit: true, @@ -125,7 +125,7 @@ export function writeTargetless({ wikiData }) { })}`, })}


    -

    ${language.$("listingIndex.exploreList")}

    +

    ${language.$('listingIndex.exploreList')}

    ${generateLinkIndexForListings(null, false, { link, language, @@ -143,7 +143,7 @@ export function writeTargetless({ wikiData }) { }), }, - nav: { simple: true }, + nav: {simple: true}, }), }; @@ -154,11 +154,11 @@ export function writeTargetless({ wikiData }) { function generateSidebarForListings( currentListing, - { getLinkThemeString, link, language, wikiData } + {getLinkThemeString, link, language, wikiData} ) { return fixWS` -

    ${link.listingIndex("", { - text: language.$("listingIndex.title"), +

    ${link.listingIndex('', { + text: language.$('listingIndex.title'), })}

    ${generateLinkIndexForListings(currentListing, true, { getLinkThemeString, @@ -172,24 +172,24 @@ function generateSidebarForListings( function generateLinkIndexForListings( currentListing, forSidebar, - { getLinkThemeString, link, language, wikiData } + {getLinkThemeString, link, language, wikiData} ) { - const { listingTargetSpec, wikiInfo } = wikiData; + const {listingTargetSpec, wikiInfo} = wikiData; const filteredByCondition = listingTargetSpec - .map(({ listings, ...rest }) => ({ + .map(({listings, ...rest}) => ({ ...rest, - listings: listings.filter(({ condition: c }) => !c || c({ wikiData })), + listings: listings.filter(({condition: c}) => !c || c({wikiData})), })) - .filter(({ listings }) => listings.length > 0); + .filter(({listings}) => listings.length > 0); const genUL = (listings) => html.tag( - "ul", + 'ul', listings.map((listing) => html.tag( - "li", - { class: [listing === currentListing && "current"] }, + 'li', + {class: [listing === currentListing && 'current']}, link.listing(listing, { text: language.$(`listingPage.${listing.stringsKey}.title.short`), }) @@ -199,30 +199,30 @@ function generateLinkIndexForListings( if (forSidebar) { return filteredByCondition - .map(({ title, listings }) => + .map(({title, listings}) => html.tag( - "details", + 'details', { open: !forSidebar || listings.includes(currentListing), - class: listings.includes(currentListing) && "current", + class: listings.includes(currentListing) && 'current', }, [ html.tag( - "summary", - { style: getLinkThemeString(wikiInfo.color) }, - html.tag("span", { class: "group-name" }, title({ language })) + 'summary', + {style: getLinkThemeString(wikiInfo.color)}, + html.tag('span', {class: 'group-name'}, title({language})) ), genUL(listings), ] ) ) - .join("\n"); + .join('\n'); } else { return html.tag( - "dl", - filteredByCondition.flatMap(({ title, listings }) => [ - html.tag("dt", title({ language })), - html.tag("dd", genUL(listings)), + 'dl', + filteredByCondition.flatMap(({title, listings}) => [ + html.tag('dt', title({language})), + html.tag('dd', genUL(listings)), ]) ); } diff --git a/src/page/news.js b/src/page/news.js index b988e34..bf581e4 100644 --- a/src/page/news.js +++ b/src/page/news.js @@ -1,40 +1,40 @@ -// @format -// +/** @format */ + // News entry & index page specifications. // Imports -import fixWS from "fix-whitespace"; +import fixWS from 'fix-whitespace'; // Page exports -export function condition({ wikiData }) { +export function condition({wikiData}) { return wikiData.wikiInfo.enableNews; } -export function targets({ wikiData }) { +export function targets({wikiData}) { return wikiData.newsData; } -export function write(entry, { wikiData }) { +export function write(entry, {wikiData}) { const page = { - type: "page", - path: ["newsEntry", entry.directory], + type: 'page', + path: ['newsEntry', entry.directory], page: ({ generatePreviousNextLinks, link, language, transformMultiline, }) => ({ - title: language.$("newsEntryPage.title", { entry: entry.name }), + title: language.$('newsEntryPage.title', {entry: entry.name}), main: { content: fixWS`
    -

    ${language.$("newsEntryPage.title", { +

    ${language.$('newsEntryPage.title', { entry: entry.name, })}

    -

    ${language.$("newsEntryPage.published", { +

    ${language.$('newsEntryPage.published', { date: language.formatDate(entry.date), })}

    ${transformMultiline(entry.content)} @@ -54,19 +54,19 @@ export function write(entry, { wikiData }) { return [page]; } -export function writeTargetless({ wikiData }) { - const { newsData } = wikiData; +export function writeTargetless({wikiData}) { + const {newsData} = wikiData; const page = { - type: "page", - path: ["newsIndex"], - page: ({ link, language, transformMultiline }) => ({ - title: language.$("newsIndex.title"), + type: 'page', + path: ['newsIndex'], + page: ({link, language, transformMultiline}) => ({ + title: language.$('newsIndex.title'), main: { content: fixWS`
    -

    ${language.$("newsIndex.title")}

    +

    ${language.$('newsIndex.title')}

    ${newsData .map( (entry) => fixWS` @@ -79,19 +79,19 @@ export function writeTargetless({ wikiData }) { entry.contentShort !== entry.content && `

    ${link.newsEntry(entry, { text: language.$( - "newsIndex.entry.viewRest" + 'newsIndex.entry.viewRest' ), })}

    ` } ` ) - .join("\n")} + .join('\n')}
    `, }, - nav: { simple: true }, + nav: {simple: true}, }), }; @@ -102,9 +102,9 @@ export function writeTargetless({ wikiData }) { function generateNewsEntryNav( entry, - { generatePreviousNextLinks, link, language, wikiData } + {generatePreviousNextLinks, link, language, wikiData} ) { - const { wikiInfo, newsData } = wikiData; + const {wikiInfo, newsData} = wikiData; // The newsData list is sorted reverse chronologically (newest ones first), // so the way we find next/previous entries is flipped from normal. @@ -112,21 +112,21 @@ function generateNewsEntryNav( link, language, data: newsData.slice().reverse(), - linkKey: "newsEntry", + linkKey: 'newsEntry', }); return { - linkContainerClasses: ["nav-links-hierarchy"], + linkContainerClasses: ['nav-links-hierarchy'], links: [ - { toHome: true }, + {toHome: true}, { - path: ["localized.newsIndex"], - title: language.$("newsEntryPage.nav.news"), + path: ['localized.newsIndex'], + title: language.$('newsEntryPage.nav.news'), }, { - html: language.$("newsEntryPage.nav.entry", { + html: language.$('newsEntryPage.nav.entry', { date: language.formatDate(entry.date), - entry: link.newsEntry(entry, { class: "current" }), + entry: link.newsEntry(entry, {class: 'current'}), }), }, previousNextLinks && { diff --git a/src/page/static.js b/src/page/static.js index ecd77c9..f4a8197 100644 --- a/src/page/static.js +++ b/src/page/static.js @@ -1,24 +1,24 @@ -// @format -// +/** @format */ + // Static content page specification. (These are static pages coded into the // wiki data folder, used for a variety of purposes, e.g. wiki info, // changelog, and so on.) // Imports -import fixWS from "fix-whitespace"; +import fixWS from 'fix-whitespace'; // Page exports -export function targets({ wikiData }) { +export function targets({wikiData}) { return wikiData.staticPageData; } -export function write(staticPage, { wikiData }) { +export function write(staticPage, {wikiData}) { const page = { - type: "page", - path: ["staticPage", staticPage.directory], - page: ({ language, transformMultiline }) => ({ + type: 'page', + path: ['staticPage', staticPage.directory], + page: ({language, transformMultiline}) => ({ title: staticPage.name, stylesheet: staticPage.stylesheet, @@ -31,7 +31,7 @@ export function write(staticPage, { wikiData }) { `, }, - nav: { simple: true }, + nav: {simple: true}, }), }; diff --git a/src/page/tag.js b/src/page/tag.js index efd95e1..4b2322d 100644 --- a/src/page/tag.js +++ b/src/page/tag.js @@ -1,34 +1,34 @@ -// @format -// +/** @format */ + // Art tag page specification. // Imports -import fixWS from "fix-whitespace"; +import fixWS from 'fix-whitespace'; // Page exports -export function condition({ wikiData }) { +export function condition({wikiData}) { return wikiData.wikiInfo.enableArtTagUI; } -export function targets({ wikiData }) { +export function targets({wikiData}) { return wikiData.artTagData.filter((tag) => !tag.isContentWarning); } -export function write(tag, { wikiData }) { - const { wikiInfo } = wikiData; - const { taggedInThings: things } = tag; +export function write(tag, {wikiData}) { + const {wikiInfo} = wikiData; + const {taggedInThings: things} = tag; // Display things featuring this art tag in reverse chronological order, // sticking the most recent additions near the top! const thingsReversed = things.slice().reverse(); - const entries = thingsReversed.map((item) => ({ item })); + const entries = thingsReversed.map((item) => ({item})); const page = { - type: "page", - path: ["tag", tag.directory], + type: 'page', + path: ['tag', tag.directory], page: ({ generatePreviousNextLinks, getAlbumCover, @@ -39,14 +39,14 @@ export function write(tag, { wikiData }) { language, to, }) => ({ - title: language.$("tagPage.title", { tag: tag.name }), + title: language.$('tagPage.title', {tag: tag.name}), theme: getThemeString(tag.color), main: { - classes: ["top-index"], + classes: ['top-index'], content: fixWS` -

    ${language.$("tagPage.title", { tag: tag.name })}

    -

    ${language.$("tagPage.infoLine", { +

    ${language.$('tagPage.title', {tag: tag.name})}

    +

    ${language.$('tagPage.infoLine', { coverArts: language.countCoverArts(things.length, { unit: true, }), @@ -83,24 +83,24 @@ export function write(tag, { wikiData }) { function generateTagNav( tag, - { generatePreviousNextLinks, link, language, wikiData } + {generatePreviousNextLinks, link, language, wikiData} ) { const previousNextLinks = generatePreviousNextLinks(tag, { data: wikiData.artTagData.filter((tag) => !tag.isContentWarning), - linkKey: "tag", + linkKey: 'tag', }); return { - linkContainerClasses: ["nav-links-hierarchy"], + linkContainerClasses: ['nav-links-hierarchy'], links: [ - { toHome: true }, + {toHome: true}, wikiData.wikiInfo.enableListings && { - path: ["localized.listingIndex"], - title: language.$("listingIndex.title"), + path: ['localized.listingIndex'], + title: language.$('listingIndex.title'), }, { - html: language.$("tagPage.nav.tag", { - tag: link.tag(tag, { class: "current" }), + html: language.$('tagPage.nav.tag', { + tag: link.tag(tag, {class: 'current'}), }), }, /* diff --git a/src/page/track.js b/src/page/track.js index 5827197..a9758ec 100644 --- a/src/page/track.js +++ b/src/page/track.js @@ -1,37 +1,37 @@ -// @format -// +/** @format */ + // Track page specification. // Imports -import fixWS from "fix-whitespace"; +import fixWS from 'fix-whitespace'; import { generateAlbumChronologyLinks, generateAlbumNavLinks, generateAlbumSecondaryNav, generateAlbumSidebar, -} from "./album.js"; +} from './album.js'; -import * as html from "../util/html.js"; +import * as html from '../util/html.js'; -import { bindOpts } from "../util/sugar.js"; +import {bindOpts} from '../util/sugar.js'; import { getTrackCover, getAlbumListTag, sortChronologically, -} from "../util/wiki-data.js"; +} from '../util/wiki-data.js'; // Page exports -export function targets({ wikiData }) { +export function targets({wikiData}) { return wikiData.trackData; } -export function write(track, { wikiData }) { - const { groupData, wikiInfo } = wikiData; - const { album, referencedByTracks, referencedTracks, otherReleases } = track; +export function write(track, {wikiData}) { + const {groupData, wikiInfo} = wikiData; + const {album, referencedByTracks, referencedTracks, otherReleases} = track; const listTag = getAlbumListTag(album); @@ -50,12 +50,12 @@ export function write(track, { wikiData }) { ); } - const unbound_getTrackItem = (track, { getArtistString, link, language }) => + const unbound_getTrackItem = (track, {getArtistString, link, language}) => html.tag( - "li", - language.$("trackList.item.withArtists", { + 'li', + language.$('trackList.item.withArtists', { track: link.track(track), - by: `${language.$("trackList.item.withArtists.by", { + by: `${language.$('trackList.item.withArtists.by', { artists: getArtistString(track.artistContribs), })}`, }) @@ -63,46 +63,46 @@ export function write(track, { wikiData }) { const unbound_generateTrackList = ( tracks, - { getArtistString, link, language } + {getArtistString, link, language} ) => html.tag( - "ul", + 'ul', tracks.map((track) => - unbound_getTrackItem(track, { getArtistString, link, language }) + unbound_getTrackItem(track, {getArtistString, link, language}) ) ); const hasCommentary = track.commentary || otherReleases.some((t) => t.commentary); - const generateCommentary = ({ link, language, transformMultiline }) => + const generateCommentary = ({link, language, transformMultiline}) => transformMultiline( [ track.commentary, ...otherReleases.map((track) => track.commentary - ?.split("\n") - .filter((line) => line.replace(/<\/b>/g, "").includes(":")) + ?.split('\n') + .filter((line) => line.replace(/<\/b>/g, '').includes(':')) .map( (line) => fixWS` ${line} ${language.$( - "releaseInfo.artistCommentary.seeOriginalRelease", + 'releaseInfo.artistCommentary.seeOriginalRelease', { original: link.track(track), } )} ` ) - .join("\n") + .join('\n') ), ] .filter(Boolean) - .join("\n") + .join('\n') ); const data = { - type: "data", - path: ["track", track.directory], + type: 'data', + path: ['track', track.directory], data: ({ serializeContribs, serializeCover, @@ -145,19 +145,19 @@ export function write(track, { wikiData }) { // they don't get parsed and displayed, generally speaking), so // override the link argument so that artist "links" just show // their names. - link: { artist: (artist) => artist.name }, + link: {artist: (artist) => artist.name}, }); - if (!hasArtists && !hasCoverArtists) return ""; + if (!hasArtists && !hasCoverArtists) return ''; return language.formatString( - "trackPage.socialEmbed.body" + - [hasArtists && ".withArtists", hasCoverArtists && ".withCoverArtists"] + 'trackPage.socialEmbed.body' + + [hasArtists && '.withArtists', hasCoverArtists && '.withCoverArtists'] .filter(Boolean) - .join(""), + .join(''), Object.fromEntries( [ - hasArtists && ["artists", getArtistString(track.artistContribs)], + hasArtists && ['artists', getArtistString(track.artistContribs)], hasCoverArtists && [ - "coverArtists", + 'coverArtists', getArtistString(track.coverArtistContribs), ], ].filter(Boolean) @@ -166,8 +166,8 @@ export function write(track, { wikiData }) { }; const page = { - type: "page", - path: ["track", track.directory], + type: 'page', + path: ['track', track.directory], page: ({ absoluteTo, fancifyURL, @@ -196,24 +196,23 @@ export function write(track, { wikiData }) { const cover = getTrackCover(track); return { - title: language.$("trackPage.title", { track: track.name }), - stylesheet: getAlbumStylesheet(album, { to }), + title: language.$('trackPage.title', {track: track.name}), + stylesheet: getAlbumStylesheet(album, {to}), theme: getThemeString(track.color, [ `--album-directory: ${album.directory}`, `--track-directory: ${track.directory}`, ]), socialEmbed: { - heading: language.$("trackPage.socialEmbed.heading", { + heading: language.$('trackPage.socialEmbed.heading', { album: track.album.name, }), - headingLink: absoluteTo("localized.album", album.directory), - title: language.$("trackPage.socialEmbed.title", { + headingLink: absoluteTo('localized.album', album.directory), + title: language.$('trackPage.socialEmbed.title', { track: track.name, }), - description: getSocialEmbedDescription({ getArtistString, language }), - image: - "/" + getTrackCover(track, { to: urls.from("shared.root").to }), + description: getSocialEmbedDescription({getArtistString, language}), + image: '/' + getTrackCover(track, {to: urls.from('shared.root').to}), color: track.color, }, @@ -234,23 +233,23 @@ export function write(track, { wikiData }) { cover && generateCoverLink({ src: cover, - alt: language.$("misc.alt.trackCover"), + alt: language.$('misc.alt.trackCover'), tags: track.artTags, }) } -

    ${language.$("trackPage.title", { +

    ${language.$('trackPage.title', { track: track.name, })}

    ${[ - language.$("releaseInfo.by", { + language.$('releaseInfo.by', { artists: getArtistString(track.artistContribs, { showContrib: true, showIcons: true, }), }), track.coverArtistContribs.length && - language.$("releaseInfo.coverArtBy", { + language.$('releaseInfo.coverArtBy', { artists: getArtistString( track.coverArtistContribs, { @@ -260,45 +259,45 @@ export function write(track, { wikiData }) { ), }), track.date && - language.$("releaseInfo.released", { + language.$('releaseInfo.released', { date: language.formatDate(track.date), }), track.coverArtDate && +track.coverArtDate !== +track.date && - language.$("releaseInfo.artReleased", { + language.$('releaseInfo.artReleased', { date: language.formatDate(track.coverArtDate), }), track.duration && - language.$("releaseInfo.duration", { + language.$('releaseInfo.duration', { duration: language.formatDuration( track.duration ), }), ] .filter(Boolean) - .join("
    \n")} + .join('
    \n')}

    ${ track.urls?.length - ? language.$("releaseInfo.listenOn", { + ? language.$('releaseInfo.listenOn', { links: language.formatDisjunctionList( track.urls.map((url) => - fancifyURL(url, { language }) + fancifyURL(url, {language}) ) ), }) - : language.$("releaseInfo.listenOn.noLinks") + : language.$('releaseInfo.listenOn.noLinks') }

    ${ otherReleases.length && fixWS` -

    ${language.$("releaseInfo.alsoReleasedAs")}

    +

    ${language.$('releaseInfo.alsoReleasedAs')}

      ${otherReleases .map( (track) => fixWS`
    • ${language.$( - "releaseInfo.alsoReleasedAs.item", + 'releaseInfo.alsoReleasedAs.item', { track: link.track(track), album: link.album(track.album), @@ -306,14 +305,14 @@ export function write(track, { wikiData }) { )}
    • ` ) - .join("\n")} + .join('\n')}
    ` } ${ track.contributorContribs.length && fixWS` -

    ${language.$("releaseInfo.contributors")}

    +

    ${language.$('releaseInfo.contributors')}

      ${track.contributorContribs .map( @@ -323,18 +322,18 @@ export function write(track, { wikiData }) { showIcons: true, })}` ) - .join("\n")} + .join('\n')}
    ` } ${ referencedTracks.length && fixWS` -

    ${language.$("releaseInfo.tracksReferenced", { +

    ${language.$('releaseInfo.tracksReferenced', { track: `${track.name}`, })}

    ${html.tag( - "ul", + 'ul', referencedTracks.map(getTrackItem) )} ` @@ -342,7 +341,7 @@ export function write(track, { wikiData }) { ${ referencedByTracks.length && fixWS` -

    ${language.$("releaseInfo.tracksThatReference", { +

    ${language.$('releaseInfo.tracksThatReference', { track: `${track.name}`, })}

    ${generateTrackListDividedByGroups( @@ -358,24 +357,24 @@ export function write(track, { wikiData }) { wikiInfo.enableFlashesAndGames && flashesThatFeature.length && fixWS` -

    ${language.$("releaseInfo.flashesThatFeature", { +

    ${language.$('releaseInfo.flashesThatFeature', { track: `${track.name}`, })}

      ${flashesThatFeature - .map(({ flash, as }) => + .map(({flash, as}) => html.tag( - "li", - { class: as !== track && "rerelease" }, + 'li', + {class: as !== track && 'rerelease'}, as === track ? language.$( - "releaseInfo.flashesThatFeature.item", + 'releaseInfo.flashesThatFeature.item', { flash: link.flash(flash), } ) : language.$( - "releaseInfo.flashesThatFeature.item.asDifferentRelease", + 'releaseInfo.flashesThatFeature.item.asDifferentRelease', { flash: link.flash(flash), track: link.track(as), @@ -383,14 +382,14 @@ export function write(track, { wikiData }) { ) ) ) - .join("\n")} + .join('\n')}
    ` } ${ track.lyrics && fixWS` -

    ${language.$("releaseInfo.lyrics")}

    +

    ${language.$('releaseInfo.lyrics')}

    ${transformLyrics(track.lyrics)}
    @@ -399,7 +398,7 @@ export function write(track, { wikiData }) { ${ hasCommentary && fixWS` -

    ${language.$("releaseInfo.artistCommentary")}

    +

    ${language.$('releaseInfo.artistCommentary')}

    ${generateCommentary({ link, @@ -422,23 +421,23 @@ export function write(track, { wikiData }) { }), nav: { - linkContainerClasses: ["nav-links-hierarchy"], + linkContainerClasses: ['nav-links-hierarchy'], links: [ - { toHome: true }, + {toHome: true}, { - path: ["localized.album", album.directory], + path: ['localized.album', album.directory], title: album.name, }, - listTag === "ol" + listTag === 'ol' ? { - html: language.$("trackPage.nav.track.withNumber", { + html: language.$('trackPage.nav.track.withNumber', { number: album.tracks.indexOf(track) + 1, - track: link.track(track, { class: "current", to }), + track: link.track(track, {class: 'current', to}), }), } : { - html: language.$("trackPage.nav.track", { - track: link.track(track, { class: "current", to }), + html: language.$('trackPage.nav.track', { + track: link.track(track, {class: 'current', to}), }), }, ].filter(Boolean), diff --git a/src/repl.js b/src/repl.js index 5826d23..5a6334a 100644 --- a/src/repl.js +++ b/src/repl.js @@ -1,53 +1,53 @@ -// @format +/** @format */ -import * as os from "os"; -import * as path from "path"; -import * as repl from "repl"; -import { fileURLToPath } from "url"; -import { promisify } from "util"; +import * as os from 'os'; +import * as path from 'path'; +import * as repl from 'repl'; +import {fileURLToPath} from 'url'; +import {promisify} from 'util'; -import { quickLoadAllFromYAML } from "./data/yaml.js"; -import { logError, parseOptions } from "./util/cli.js"; -import { showAggregate } from "./util/sugar.js"; +import {quickLoadAllFromYAML} from './data/yaml.js'; +import {logError, parseOptions} from './util/cli.js'; +import {showAggregate} from './util/sugar.js'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); async function main() { const miscOptions = await parseOptions(process.argv.slice(2), { - "data-path": { - type: "value", + 'data-path': { + type: 'value', }, - "show-traces": { - type: "flag", + 'show-traces': { + type: 'flag', }, - "no-history": { - type: "flag", + 'no-history': { + type: 'flag', }, }); - const dataPath = miscOptions["data-path"] || process.env.HSMUSIC_DATA; - const showAggregateTraces = miscOptions["show-traces"] ?? false; - const disableHistory = miscOptions["no-history"] ?? false; + const dataPath = miscOptions['data-path'] || process.env.HSMUSIC_DATA; + const showAggregateTraces = miscOptions['show-traces'] ?? false; + const disableHistory = miscOptions['no-history'] ?? false; if (!dataPath) { logError`Expected --data-path option or HSMUSIC_DATA to be set`; return; } - console.log("HSMusic data REPL"); + console.log('HSMusic data REPL'); const wikiData = await quickLoadAllFromYAML(dataPath); const replServer = repl.start(); - Object.assign(replServer.context, wikiData, { wikiData, WD: wikiData }); + Object.assign(replServer.context, wikiData, {wikiData, WD: wikiData}); if (disableHistory) { console.log(`\rInput history disabled (--no-history provided)`); replServer.displayPrompt(true); } else { - const historyFile = path.join(os.homedir(), ".hsmusic_repl_history"); + const historyFile = path.join(os.homedir(), '.hsmusic_repl_history'); replServer.setupHistory(historyFile, (err) => { if (err) { console.error( diff --git a/src/static/client.js b/src/static/client.js index d68358b..8342eb1 100644 --- a/src/static/client.js +++ b/src/static/client.js @@ -1,5 +1,5 @@ -// @format -// +/** @format */ + // This is the JS file that gets loaded on the client! It's only really used for // the random track feature right now - the idea is we only use it for stuff // that cannot 8e done at static-site compile time, 8y its fundamentally @@ -7,9 +7,9 @@ // // Upd8: As of 04/02/2021, it's now used for info cards too! Nice. -import { getColors } from "../util/colors.js"; +import {getColors} from '../util/colors.js'; -import { getArtistNumContributions } from "../util/wiki-data.js"; +import {getArtistNumContributions} from '../util/wiki-data.js'; let albumData, artistData, flashData; let officialAlbumData, fandomAlbumData, artistNames; @@ -18,25 +18,25 @@ let ready = false; // Localiz8tion nonsense ---------------------------------- -const language = document.documentElement.getAttribute("lang"); +const language = document.documentElement.getAttribute('lang'); let list; -if (typeof Intl === "object" && typeof Intl.ListFormat === "function") { +if (typeof Intl === 'object' && typeof Intl.ListFormat === 'function') { const getFormat = (type) => { - const formatter = new Intl.ListFormat(language, { type }); + const formatter = new Intl.ListFormat(language, {type}); return formatter.format.bind(formatter); }; list = { - conjunction: getFormat("conjunction"), - disjunction: getFormat("disjunction"), - unit: getFormat("unit"), + conjunction: getFormat('conjunction'), + disjunction: getFormat('disjunction'), + unit: getFormat('unit'), }; } else { // Not a gr8 mock we've got going here, 8ut it's *mostly* language-free. // We use the same mock for every list 'cuz we don't have any of the // necessary CLDR info to appropri8tely distinguish 8etween them. - const arbitraryMock = (array) => array.join(", "); + const arbitraryMock = (array) => array.join(', '); list = { conjunction: arbitraryMock, @@ -47,8 +47,8 @@ if (typeof Intl === "object" && typeof Intl.ListFormat === "function") { // Miscellaneous helpers ---------------------------------- -function rebase(href, rebaseKey = "rebaseLocalized") { - const relative = (document.documentElement.dataset[rebaseKey] || ".") + "/"; +function rebase(href, rebaseKey = 'rebaseLocalized') { + const relative = (document.documentElement.dataset[rebaseKey] || '.') + '/'; if (relative) { return relative + href; } else { @@ -65,16 +65,16 @@ function cssProp(el, key) { } function getRefDirectory(ref) { - return ref.split(":")[1]; + return ref.split(':')[1]; } function getAlbum(el) { - const directory = cssProp(el, "--album-directory"); + const directory = cssProp(el, '--album-directory'); return albumData.find((album) => album.directory === directory); } function getFlash(el) { - const directory = cssProp(el, "--flash-directory"); + const directory = cssProp(el, '--flash-directory'); return flashData.find((flash) => flash.directory === directory); } @@ -88,17 +88,17 @@ const openFlash = (d) => rebase(`flash/${d}`); function getTrackListAndIndex() { const album = getAlbum(document.body); - const directory = cssProp(document.body, "--track-directory"); + const directory = cssProp(document.body, '--track-directory'); if (!directory && !album) return {}; - if (!directory) return { list: album.tracks }; + if (!directory) return {list: album.tracks}; const trackIndex = album.tracks.findIndex( (track) => track.directory === directory ); - return { list: album.tracks, index: trackIndex }; + return {list: album.tracks, index: trackIndex}; } function openRandomTrack() { - const { list } = getTrackListAndIndex(); + const {list} = getTrackListAndIndex(); if (!list) return; return openTrack(pick(list)); } @@ -106,38 +106,38 @@ function openRandomTrack() { function getFlashListAndIndex() { const list = flashData.filter((flash) => !flash.act8r8k); const flash = getFlash(document.body); - if (!flash) return { list }; + if (!flash) return {list}; const flashIndex = list.indexOf(flash); - return { list, index: flashIndex }; + return {list, index: flashIndex}; } // TODO: This should also use urlSpec. function fetchData(type, directory) { - return fetch(rebase(`${type}/${directory}/data.json`, "rebaseData")).then( + return fetch(rebase(`${type}/${directory}/data.json`, 'rebaseData')).then( (res) => res.json() ); } // JS-based links ----------------------------------------- -for (const a of document.body.querySelectorAll("[data-random]")) { - a.addEventListener("click", (evt) => { +for (const a of document.body.querySelectorAll('[data-random]')) { + a.addEventListener('click', (evt) => { if (!ready) { evt.preventDefault(); return; } setTimeout(() => { - a.href = rebase("js-disabled"); + a.href = rebase('js-disabled'); }); switch (a.dataset.random) { - case "album": + case 'album': return (a.href = openAlbum(pick(albumData).directory)); - case "album-in-fandom": + case 'album-in-fandom': return (a.href = openAlbum(pick(fandomAlbumData).directory)); - case "album-in-official": + case 'album-in-official': return (a.href = openAlbum(pick(officialAlbumData).directory)); - case "track": + case 'track': return (a.href = openTrack( getRefDirectory( pick( @@ -145,9 +145,9 @@ for (const a of document.body.querySelectorAll("[data-random]")) { ) ) )); - case "track-in-album": + case 'track-in-album': return (a.href = openTrack(getRefDirectory(pick(getAlbum(a).tracks)))); - case "track-in-fandom": + case 'track-in-fandom': return (a.href = openTrack( getRefDirectory( pick( @@ -158,7 +158,7 @@ for (const a of document.body.querySelectorAll("[data-random]")) { ) ) )); - case "track-in-official": + case 'track-in-official': return (a.href = openTrack( getRefDirectory( pick( @@ -169,9 +169,9 @@ for (const a of document.body.querySelectorAll("[data-random]")) { ) ) )); - case "artist": + case 'artist': return (a.href = openArtist(pick(artistData).directory)); - case "artist-more-than-one-contrib": + case 'artist-more-than-one-contrib': return (a.href = openArtist( pick( artistData.filter((artist) => getArtistNumContributions(artist) > 1) @@ -181,51 +181,51 @@ for (const a of document.body.querySelectorAll("[data-random]")) { }); } -const next = document.getElementById("next-button"); -const previous = document.getElementById("previous-button"); -const random = document.getElementById("random-button"); +const next = document.getElementById('next-button'); +const previous = document.getElementById('previous-button'); +const random = document.getElementById('random-button'); const prependTitle = (el, prepend) => { - const existing = el.getAttribute("title"); + const existing = el.getAttribute('title'); if (existing) { - el.setAttribute("title", prepend + " " + existing); + el.setAttribute('title', prepend + ' ' + existing); } else { - el.setAttribute("title", prepend); + el.setAttribute('title', prepend); } }; -if (next) prependTitle(next, "(Shift+N)"); -if (previous) prependTitle(previous, "(Shift+P)"); -if (random) prependTitle(random, "(Shift+R)"); +if (next) prependTitle(next, '(Shift+N)'); +if (previous) prependTitle(previous, '(Shift+P)'); +if (random) prependTitle(random, '(Shift+R)'); -document.addEventListener("keypress", (event) => { +document.addEventListener('keypress', (event) => { if (event.shiftKey) { - if (event.charCode === "N".charCodeAt(0)) { + if (event.charCode === 'N'.charCodeAt(0)) { if (next) next.click(); - } else if (event.charCode === "P".charCodeAt(0)) { + } else if (event.charCode === 'P'.charCodeAt(0)) { if (previous) previous.click(); - } else if (event.charCode === "R".charCodeAt(0)) { + } else if (event.charCode === 'R'.charCodeAt(0)) { if (random && ready) random.click(); } } }); -for (const reveal of document.querySelectorAll(".reveal")) { - reveal.addEventListener("click", (event) => { - if (!reveal.classList.contains("revealed")) { - reveal.classList.add("revealed"); +for (const reveal of document.querySelectorAll('.reveal')) { + reveal.addEventListener('click', (event) => { + if (!reveal.classList.contains('revealed')) { + reveal.classList.add('revealed'); event.preventDefault(); event.stopPropagation(); } }); } -const elements1 = document.getElementsByClassName("js-hide-once-data"); -const elements2 = document.getElementsByClassName("js-show-once-data"); +const elements1 = document.getElementsByClassName('js-hide-once-data'); +const elements2 = document.getElementsByClassName('js-show-once-data'); -for (const element of elements1) element.style.display = "block"; +for (const element of elements1) element.style.display = 'block'; -fetch(rebase("data.json", "rebaseShared")) +fetch(rebase('data.json', 'rebaseShared')) .then((data) => data.json()) .then((data) => { albumData = data.albumData; @@ -233,17 +233,17 @@ fetch(rebase("data.json", "rebaseShared")) flashData = data.flashData; officialAlbumData = albumData.filter((album) => - album.groups.includes("group:official") + album.groups.includes('group:official') ); fandomAlbumData = albumData.filter( - (album) => !album.groups.includes("group:official") + (album) => !album.groups.includes('group:official') ); artistNames = artistData .filter((artist) => !artist.alias) .map((artist) => artist.name); - for (const element of elements1) element.style.display = "none"; - for (const element of elements2) element.style.display = "block"; + for (const element of elements1) element.style.display = 'none'; + for (const element of elements2) element.style.display = 'block'; ready = true; }); @@ -260,13 +260,13 @@ let endFastHoverTimeout = null; function colorLink(a, color) { if (color) { - const { primary, dim } = getColors(color); - a.style.setProperty("--primary-color", primary); - a.style.setProperty("--dim-color", dim); + const {primary, dim} = getColors(color); + a.style.setProperty('--primary-color', primary); + a.style.setProperty('--dim-color', dim); } } -function link(a, type, { name, directory, color }) { +function link(a, type, {name, directory, color}) { colorLink(a, color); a.innerText = name; a.href = getLinkHref(type, directory); @@ -284,14 +284,14 @@ function joinElements(type, elements) { } const infoCard = (() => { - const container = document.getElementById("info-card-container"); + const container = document.getElementById('info-card-container'); let cancelShow = false; let hideTimeout = null; let showing = false; - container.addEventListener("mouseenter", cancelHide); - container.addEventListener("mouseleave", readyHide); + container.addEventListener('mouseenter', cancelHide); + container.addEventListener('mouseleave', readyHide); function show(type, target) { cancelShow = false; @@ -307,91 +307,91 @@ const infoCard = (() => { const rect = target.getBoundingClientRect(); - container.style.setProperty("--primary-color", data.color); + container.style.setProperty('--primary-color', data.color); - container.style.top = window.scrollY + rect.bottom + "px"; - container.style.left = window.scrollX + rect.left + "px"; + container.style.top = window.scrollY + rect.bottom + 'px'; + container.style.left = window.scrollX + rect.left + 'px'; // Use a short timeout to let a currently hidden (or not yet shown) // info card teleport to the position set a8ove. (If it's currently // shown, it'll transition to that position.) setTimeout(() => { - container.classList.remove("hide"); - container.classList.add("show"); + container.classList.remove('hide'); + container.classList.add('show'); }, 50); // 8asic details. - const nameLink = container.querySelector(".info-card-name a"); - link(nameLink, "track", data); + const nameLink = container.querySelector('.info-card-name a'); + link(nameLink, 'track', data); - const albumLink = container.querySelector(".info-card-album a"); - link(albumLink, "album", data.album); + const albumLink = container.querySelector('.info-card-album a'); + link(albumLink, 'album', data.album); - const artistSpan = container.querySelector(".info-card-artists span"); + const artistSpan = container.querySelector('.info-card-artists span'); artistSpan.innerHTML = joinElements( - "conjunction", - data.artists.map(({ artist }) => { - const a = document.createElement("a"); - a.href = getLinkHref("artist", artist.directory); + 'conjunction', + data.artists.map(({artist}) => { + const a = document.createElement('a'); + a.href = getLinkHref('artist', artist.directory); a.innerText = artist.name; return a; }) ); const coverArtistParagraph = container.querySelector( - ".info-card-cover-artists" + '.info-card-cover-artists' ); - const coverArtistSpan = coverArtistParagraph.querySelector("span"); + const coverArtistSpan = coverArtistParagraph.querySelector('span'); if (data.coverArtists.length) { - coverArtistParagraph.style.display = "block"; + coverArtistParagraph.style.display = 'block'; coverArtistSpan.innerHTML = joinElements( - "conjunction", - data.coverArtists.map(({ artist }) => { - const a = document.createElement("a"); - a.href = getLinkHref("artist", artist.directory); + 'conjunction', + data.coverArtists.map(({artist}) => { + const a = document.createElement('a'); + a.href = getLinkHref('artist', artist.directory); a.innerText = artist.name; return a; }) ); } else { - coverArtistParagraph.style.display = "none"; + coverArtistParagraph.style.display = 'none'; } // Cover art. const [containerNoReveal, containerReveal] = [ - container.querySelector(".info-card-art-container.no-reveal"), - container.querySelector(".info-card-art-container.reveal"), + container.querySelector('.info-card-art-container.no-reveal'), + container.querySelector('.info-card-art-container.reveal'), ]; const [containerShow, containerHide] = data.cover.warnings.length ? [containerReveal, containerNoReveal] : [containerNoReveal, containerReveal]; - containerHide.style.display = "none"; - containerShow.style.display = "block"; + containerHide.style.display = 'none'; + containerShow.style.display = 'block'; - const img = containerShow.querySelector(".info-card-art"); - img.src = rebase(data.cover.paths.small, "rebaseMedia"); + const img = containerShow.querySelector('.info-card-art'); + img.src = rebase(data.cover.paths.small, 'rebaseMedia'); - const imgLink = containerShow.querySelector("a"); + const imgLink = containerShow.querySelector('a'); colorLink(imgLink, data.color); - imgLink.href = rebase(data.cover.paths.original, "rebaseMedia"); + imgLink.href = rebase(data.cover.paths.original, 'rebaseMedia'); if (containerShow === containerReveal) { - const cw = containerShow.querySelector(".info-card-art-warnings"); + const cw = containerShow.querySelector('.info-card-art-warnings'); cw.innerText = list.unit(data.cover.warnings); - const reveal = containerShow.querySelector(".reveal"); - reveal.classList.remove("revealed"); + const reveal = containerShow.querySelector('.reveal'); + reveal.classList.remove('revealed'); } }); } function hide() { - container.classList.remove("show"); - container.classList.add("hide"); + container.classList.remove('show'); + container.classList.add('hide'); cancelShow = true; showing = false; } @@ -452,7 +452,7 @@ function makeInfoCardLinkHandlers(type) { } const infoCardLinkHandlers = { - track: makeInfoCardLinkHandlers("track"), + track: makeInfoCardLinkHandlers('track'), }; function addInfoCardLinkHandlers(type) { @@ -471,5 +471,5 @@ function addInfoCardLinkHandlers(type) { // localStorage.tryInfoCards = true; // if (localStorage.tryInfoCards) { - addInfoCardLinkHandlers("track"); + addInfoCardLinkHandlers('track'); } diff --git a/src/static/lazy-loading.js b/src/static/lazy-loading.js index 5ca9fb3..8f3e8cf 100644 --- a/src/static/lazy-loading.js +++ b/src/static/lazy-loading.js @@ -1,5 +1,5 @@ -// @format -// +/** @format */ + // Lazy loading! Roll your own. Woot. // This file includes a 8unch of fall8acks and stuff like that, and is written // with fairly Olden JavaScript(TM), so as to work on pretty much any 8rowser @@ -27,15 +27,15 @@ function lazyLoadMain() { // we'd 8e mutating its value just 8y interacting with the DOM elements it // contains. A while loop works just fine, even though you'd think reading // over this code that this would 8e an infinitely hanging loop. It isn't! - var elements = document.getElementsByClassName("js-hide"); + var elements = document.getElementsByClassName('js-hide'); while (elements.length) { - elements[0].classList.remove("js-hide"); + elements[0].classList.remove('js-hide'); } - var lazyElements = document.getElementsByClassName("lazy"); + var lazyElements = document.getElementsByClassName('lazy'); if (window.IntersectionObserver) { observer = new IntersectionObserver(lazyLoad, { - rootMargin: "200px", + rootMargin: '200px', threshold: 1.0, }); for (var i = 0; i < lazyElements.length; i++) { @@ -44,10 +44,10 @@ function lazyLoadMain() { } else { for (var i = 0; i < lazyElements.length; i++) { var element = lazyElements[i]; - var original = element.getAttribute("data-original"); - element.setAttribute("src", original); + var original = element.getAttribute('data-original'); + element.setAttribute('src', original); } } } -document.addEventListener("DOMContentLoaded", lazyLoadMain); +document.addEventListener('DOMContentLoaded', lazyLoadMain); diff --git a/src/upd8.js b/src/upd8.js index cff4313..8a5a287 100755 --- a/src/upd8.js +++ b/src/upd8.js @@ -1,6 +1,5 @@ #!/usr/bin/env node - -// @format +/** @format */ // HEY N8RDS! // @@ -33,18 +32,18 @@ // Oh yeah, like. Just run this through some relatively recent version of // node.js and you'll 8e fine. ...Within the project root. O8viously. -import * as path from "path"; -import { promisify } from "util"; -import { fileURLToPath } from "url"; +import * as path from 'path'; +import {promisify} from 'util'; +import {fileURLToPath} from 'url'; // I made this dependency myself! A long, long time ago. It is pro8a8ly my // most useful li8rary ever. I'm not sure 8esides me actually uses it, though. -import fixWS from "fix-whitespace"; +import fixWS from 'fix-whitespace'; // Wait nevermind, I forgot a8out why-do-kids-love-the-taste-of-cinnamon-toast- // crunch. THAT is my 8est li8rary. // It stands for "HTML Entities", apparently. Cursed. -import he from "he"; +import he from 'he'; import { copyFile, @@ -54,25 +53,25 @@ import { symlink, writeFile, unlink, -} from "fs/promises"; +} from 'fs/promises'; -import { inspect as nodeInspect } from "util"; +import {inspect as nodeInspect} from 'util'; -import genThumbs from "./gen-thumbs.js"; -import { listingSpec, listingTargetSpec } from "./listing-spec.js"; -import urlSpec from "./url-spec.js"; -import * as pageSpecs from "./page/index.js"; +import genThumbs from './gen-thumbs.js'; +import {listingSpec, listingTargetSpec} from './listing-spec.js'; +import urlSpec from './url-spec.js'; +import * as pageSpecs from './page/index.js'; -import find, { bindFind } from "./util/find.js"; -import * as html from "./util/html.js"; -import unbound_link, { getLinkThemeString } from "./util/link.js"; -import { findFiles } from "./util/io.js"; +import find, {bindFind} from './util/find.js'; +import * as html from './util/html.js'; +import unbound_link, {getLinkThemeString} from './util/link.js'; +import {findFiles} from './util/io.js'; -import CacheableObject from "./data/cacheable-object.js"; +import CacheableObject from './data/cacheable-object.js'; -import { serializeThings } from "./data/serialize.js"; +import {serializeThings} from './data/serialize.js'; -import { Language } from "./data/things.js"; +import {Language} from './data/things.js'; import { filterDuplicateDirectories, @@ -81,7 +80,7 @@ import { loadAndProcessDataDocuments, sortWikiDataArrays, WIKI_INFO_FILE, -} from "./data/yaml.js"; +} from './data/yaml.js'; import { fancifyFlashURL, @@ -103,7 +102,7 @@ import { getRevealStringFromWarnings, getThemeString, iconifyURL, -} from "./misc-templates.js"; +} from './misc-templates.js'; import { color, @@ -114,9 +113,9 @@ import { parseOptions, progressPromiseAll, ENABLE_COLOR, -} from "./util/cli.js"; +} from './util/cli.js'; -import { validateReplacerSpec, transformInline } from "./util/replacer.js"; +import {validateReplacerSpec, transformInline} from './util/replacer.js'; import { chunkByConditions, @@ -130,7 +129,7 @@ import { getKebabCase, getTotalDuration, getTrackCover, -} from "./util/wiki-data.js"; +} from './util/wiki-data.js'; import { serializeContribs, @@ -139,7 +138,7 @@ import { serializeGroupsForTrack, serializeImagePaths, serializeLink, -} from "./util/serialize.js"; +} from './util/serialize.js'; import { bindOpts, @@ -155,23 +154,23 @@ import { unique, withAggregate, withEntries, -} from "./util/sugar.js"; +} from './util/sugar.js'; -import { generateURLs, thumb } from "./util/urls.js"; +import {generateURLs, thumb} from './util/urls.js'; // Pensive emoji! import { FANDOM_GROUP_DIRECTORY, OFFICIAL_GROUP_DIRECTORY, -} from "./util/magic-constants.js"; +} from './util/magic-constants.js'; -import FileSizePreloader from "./file-size-preloader.js"; +import FileSizePreloader from './file-size-preloader.js'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const CACHEBUST = 10; -const DEFAULT_STRINGS_FILE = "strings-default.json"; +const DEFAULT_STRINGS_FILE = 'strings-default.json'; // Code that's common 8etween the 8uild code (i.e. upd8.js) and gener8ted // site code should 8e put here. Which, uh, ~~only really means this one @@ -180,20 +179,20 @@ const DEFAULT_STRINGS_FILE = "strings-default.json"; // Rather than hard code it, anything in this directory can 8e shared across // 8oth ends of the code8ase. // (This gets symlinked into the --data-path directory.) -const UTILITY_DIRECTORY = "util"; +const UTILITY_DIRECTORY = 'util'; // Code that's used only in the static site! CSS, cilent JS, etc. // (This gets symlinked into the --data-path directory.) -const STATIC_DIRECTORY = "static"; +const STATIC_DIRECTORY = 'static'; // This exists adjacent to index.html for any page with oEmbed metadata. -const OEMBED_JSON_FILE = "oembed.json"; +const OEMBED_JSON_FILE = 'oembed.json'; // Automatically copied (if present) from media directory to site root. -const FAVICON_FILE = "favicon.ico"; +const FAVICON_FILE = 'favicon.ico'; function inspect(value) { - return nodeInspect(value, { colors: ENABLE_COLOR }); + return nodeInspect(value, {colors: ENABLE_COLOR}); } // Shared varia8les! These are more efficient to access than a shared varia8le @@ -222,38 +221,38 @@ function splitLines(text) { const replacerSpec = { album: { - find: "album", - link: "album", + find: 'album', + link: 'album', }, - "album-commentary": { - find: "album", - link: "albumCommentary", + 'album-commentary': { + find: 'album', + link: 'albumCommentary', }, artist: { - find: "artist", - link: "artist", + find: 'artist', + link: 'artist', }, - "artist-gallery": { - find: "artist", - link: "artistGallery", + 'artist-gallery': { + find: 'artist', + link: 'artistGallery', }, - "commentary-index": { + 'commentary-index': { find: null, - link: "commentaryIndex", + link: 'commentaryIndex', }, date: { find: null, value: (ref) => new Date(ref), - html: (date, { language }) => + html: (date, {language}) => ``, }, flash: { - find: "flash", - link: "flash", + find: 'flash', + link: 'flash', transformName(name, node, input) { const nextCharacter = input[node.iEnd]; const lastCharacter = name[name.length - 1]; - if (![" ", "\n", "<"].includes(nextCharacter) && lastCharacter === ".") { + if (![' ', '\n', '<'].includes(nextCharacter) && lastCharacter === '.') { return name.slice(0, -1); } else { return name; @@ -261,69 +260,69 @@ const replacerSpec = { }, }, group: { - find: "group", - link: "groupInfo", + find: 'group', + link: 'groupInfo', }, - "group-gallery": { - find: "group", - link: "groupGallery", + 'group-gallery': { + find: 'group', + link: 'groupGallery', }, home: { find: null, - link: "home", + link: 'home', }, - "listing-index": { + 'listing-index': { find: null, - link: "listingIndex", + link: 'listingIndex', }, listing: { - find: "listing", - link: "listing", + find: 'listing', + link: 'listing', }, media: { find: null, - link: "media", + link: 'media', }, - "news-index": { + 'news-index': { find: null, - link: "newsIndex", + link: 'newsIndex', }, - "news-entry": { - find: "newsEntry", - link: "newsEntry", + 'news-entry': { + find: 'newsEntry', + link: 'newsEntry', }, root: { find: null, - link: "root", + link: 'root', }, site: { find: null, - link: "site", + link: 'site', }, static: { - find: "staticPage", - link: "staticPage", + find: 'staticPage', + link: 'staticPage', }, string: { find: null, value: (ref) => ref, - html: (ref, { language, args }) => language.$(ref, args), + html: (ref, {language, args}) => language.$(ref, args), }, tag: { - find: "artTag", - link: "tag", + find: 'artTag', + link: 'tag', }, track: { - find: "track", - link: "track", + find: 'track', + link: 'track', }, }; -if (!validateReplacerSpec(replacerSpec, { find, link: unbound_link })) { +if (!validateReplacerSpec(replacerSpec, {find, link: unbound_link})) { process.exit(); } -function parseAttributes(string, { to }) { +function parseAttributes(string, {to}) { const attributes = Object.create(null); const skipWhitespace = (i) => { const ws = /\s/; @@ -345,7 +344,7 @@ function parseAttributes(string, { to }) { const aEnd = i + string.slice(i).match(/[\s=]|$/).index; const attribute = string.slice(aStart, aEnd); i = skipWhitespace(aEnd); - if (string[i] === "=") { + if (string[i] === '=') { i = skipWhitespace(i + 1); let end, endOffset; if (string[i] === '"' || string[i] === "'") { @@ -353,15 +352,15 @@ function parseAttributes(string, { to }) { endOffset = 1; i++; } else { - end = "\\s"; + end = '\\s'; endOffset = 0; } const vStart = i; const vEnd = i + string.slice(i).match(new RegExp(`${end}|$`)).index; const value = string.slice(vStart, vEnd); i = vEnd + endOffset; - if (attribute === "src" && value.startsWith("media/")) { - attributes[attribute] = to("media.path", value.slice("media/".length)); + if (attribute === 'src' && value.startsWith('media/')) { + attributes[attribute] = to('media.path', value.slice('media/'.length)); } else { attributes[attribute] = value; } @@ -372,9 +371,9 @@ function parseAttributes(string, { to }) { return Object.fromEntries( Object.entries(attributes).map(([key, val]) => [ key, - val === "true" + val === 'true' ? true - : val === "false" + : val === 'false' ? false : val === key ? true @@ -386,13 +385,13 @@ function parseAttributes(string, { to }) { function joinLineBreaks(sourceLines) { const outLines = []; - let lineSoFar = ""; + let lineSoFar = ''; for (let i = 0; i < sourceLines.length; i++) { const line = sourceLines[i]; lineSoFar += line; - if (!line.endsWith("
    ")) { + if (!line.endsWith('
    ')) { outLines.push(lineSoFar); - lineSoFar = ""; + lineSoFar = ''; } } @@ -403,14 +402,14 @@ function joinLineBreaks(sourceLines) { return outLines; } -function transformMultiline(text, { parseAttributes, transformInline }) { +function transformMultiline(text, {parseAttributes, transformInline}) { // Heck yes, HTML magics. text = transformInline(text.trim()); const outLines = []; - const indentString = " ".repeat(4); + const indentString = ' '.repeat(4); let levelIndents = []; const openLevel = (indent) => { @@ -418,13 +417,13 @@ function transformMultiline(text, { parseAttributes, transformInline }) { // correct, we have to append the
      at the end of the existing // previous
    • const previousLine = outLines[outLines.length - 1]; - if (previousLine?.endsWith("
    • ")) { + if (previousLine?.endsWith('')) { // we will re-close the
    • later - outLines[outLines.length - 1] = previousLine.slice(0, -5) + "
        "; + outLines[outLines.length - 1] = previousLine.slice(0, -5) + '
          '; } else { // if the previous line isn't a list item, this is the opening of // the first list level, so no need for indent - outLines.push("
            "); + outLines.push('
              '); } levelIndents.push(indent); }; @@ -432,10 +431,10 @@ function transformMultiline(text, { parseAttributes, transformInline }) { levelIndents.pop(); if (levelIndents.length) { // closing a sublist, so close the list item containing it too - outLines.push(indentString.repeat(levelIndents.length) + "
            "); + outLines.push(indentString.repeat(levelIndents.length) + '
          '); } else { // closing the final list level! no need for indent here - outLines.push("
        "); + outLines.push('
      '); } }; @@ -448,19 +447,19 @@ function transformMultiline(text, { parseAttributes, transformInline }) { let lines = splitLines(text); lines = joinLineBreaks(lines); for (let line of lines) { - const imageLine = line.startsWith("/g, (match, attributes) => img({ lazy: true, link: true, - thumb: "medium", + thumb: 'medium', ...parseAttributes(attributes), }) ); let indentThisLine = 0; let lineContent = line; - let lineTag = "p"; + let lineTag = 'p'; const listMatch = line.match(/^( *)- *(.*)$/); if (listMatch) { @@ -498,7 +497,7 @@ function transformMultiline(text, { parseAttributes, transformInline }) { // finally, set variables for appending content line indentThisLine = levelIndents.length; lineContent = listMatch[2]; - lineTag = "li"; + lineTag = 'li'; } else { // not a list item! close any existing list levels while (levelIndents.length) closeLevel(); @@ -510,27 +509,27 @@ function transformMultiline(text, { parseAttributes, transformInline }) { // is a quote! open a blockquote tag if it doesnt already exist if (!inBlockquote) { inBlockquote = true; - outLines.push("
      "); + outLines.push('
      '); } indentThisLine = 1; lineContent = quoteMatch[1]; } else if (inBlockquote) { // not a quote! close a blockquote tag if it exists inBlockquote = false; - outLines.push("
      "); + outLines.push('
      '); } // let some escaped symbols display as the normal symbol, since the // point of escaping them is just to avoid having them be treated as // syntax markers! if (lineContent.match(/( *)\\-/)) { - lineContent = lineContent.replace("\\-", "-"); + lineContent = lineContent.replace('\\-', '-'); } else if (lineContent.match(/( *)\\>/)) { - lineContent = lineContent.replace("\\>", ">"); + lineContent = lineContent.replace('\\>', '>'); } } - if (lineTag === "p") { + if (lineTag === 'p') { // certain inline element tags should still be postioned within a // paragraph; other elements (e.g. headings) should be added as-is const elementMatch = line.match(/^<(.*?)[ >]/); @@ -538,40 +537,40 @@ function transformMultiline(text, { parseAttributes, transformInline }) { elementMatch && !imageLine && ![ - "a", - "abbr", - "b", - "bdo", - "br", - "cite", - "code", - "data", - "datalist", - "del", - "dfn", - "em", - "i", - "img", - "ins", - "kbd", - "mark", - "output", - "picture", - "q", - "ruby", - "samp", - "small", - "span", - "strong", - "sub", - "sup", - "svg", - "time", - "var", - "wbr", + 'a', + 'abbr', + 'b', + 'bdo', + 'br', + 'cite', + 'code', + 'data', + 'datalist', + 'del', + 'dfn', + 'em', + 'i', + 'img', + 'ins', + 'kbd', + 'mark', + 'output', + 'picture', + 'q', + 'ruby', + 'samp', + 'small', + 'span', + 'strong', + 'sub', + 'sup', + 'svg', + 'time', + 'var', + 'wbr', ].includes(elementMatch[1]) ) { - lineTag = ""; + lineTag = ''; } } @@ -592,43 +591,43 @@ function transformMultiline(text, { parseAttributes, transformInline }) { // if still in a blockquote, close its tag if (inBlockquote) { inBlockquote = false; - outLines.push("
    "); + outLines.push(''); } - return outLines.join("\n"); + return outLines.join('\n'); } -function transformLyrics(text, { transformInline, transformMultiline }) { +function transformLyrics(text, {transformInline, transformMultiline}) { // Different from transformMultiline 'cuz it joins multiple lines together // with line 8reaks (
    ); transformMultiline treats each line as its own // complete paragraph (or list, etc). // If it looks like old data, then like, oh god. // Use the normal transformMultiline tool. - if (text.includes(" outLines.push(`

    ${buildLine}

    `); const outLines = []; - for (const line of text.split("\n")) { + for (const line of text.split('\n')) { if (line.length) { if (buildLine.length) { - buildLine += "
    "; + buildLine += '
    '; } buildLine += line; } else if (buildLine.length) { addLine(); - buildLine = ""; + buildLine = ''; } } if (buildLine.length) { addLine(); } - return outLines.join("\n"); + return outLines.join('\n'); } function stringifyThings(thingData) { @@ -638,7 +637,7 @@ function stringifyThings(thingData) { function img({ src, alt, - noSrcText = "", + noSrcText = '', thumb: thumbKey, reveal, id, @@ -650,13 +649,13 @@ function img({ square = false, }) { const willSquare = square; - const willLink = typeof link === "string" || link; + const willLink = typeof link === 'string' || link; const originalSrc = src; const thumbSrc = src && (thumbKey ? thumb[thumbKey](src) : src); const imgAttributes = html.attributes({ - id: link ? "" : id, + id: link ? '' : id, class: className, alt, width, @@ -701,21 +700,21 @@ function img({ } if (willSquare) { - wrapped = html.tag("div", { class: "square-content" }, wrapped); + wrapped = html.tag('div', {class: 'square-content'}, wrapped); wrapped = html.tag( - "div", - { class: ["square", hide && !willLink && "js-hide"] }, + 'div', + {class: ['square', hide && !willLink && 'js-hide']}, wrapped ); } if (willLink) { wrapped = html.tag( - "a", + 'a', { id, - class: ["box", hide && "js-hide"], - href: typeof link === "string" ? link : originalSrc, + class: ['box', hide && 'js-hide'], + href: typeof link === 'string' ? link : originalSrc, }, wrapped ); @@ -727,16 +726,16 @@ function img({ function validateWritePath(path, urlGroup) { if (!Array.isArray(path)) { - return { error: `Expected array, got ${path}` }; + return {error: `Expected array, got ${path}`}; } - const { paths } = urlGroup; + const {paths} = urlGroup; const definedKeys = Object.keys(paths); const specifiedKey = path[0]; if (!definedKeys.includes(specifiedKey)) { - return { error: `Specified key ${specifiedKey} isn't defined` }; + return {error: `Specified key ${specifiedKey} isn't defined`}; } const expectedArgs = paths[specifiedKey].match(/<>/g)?.length ?? 0; @@ -748,54 +747,54 @@ function validateWritePath(path, urlGroup) { }; } - return { success: true }; + return {success: true}; } function validateWriteObject(obj) { - if (typeof obj !== "object") { - return { error: `Expected object, got ${typeof obj}` }; + if (typeof obj !== 'object') { + return {error: `Expected object, got ${typeof obj}`}; } - if (typeof obj.type !== "string") { - return { error: `Expected type to be string, got ${obj.type}` }; + if (typeof obj.type !== 'string') { + return {error: `Expected type to be string, got ${obj.type}`}; } switch (obj.type) { - case "legacy": { - if (typeof obj.write !== "function") { - return { error: `Expected write to be string, got ${obj.write}` }; + case 'legacy': { + if (typeof obj.write !== 'function') { + return {error: `Expected write to be string, got ${obj.write}`}; } break; } - case "page": { + case 'page': { const path = validateWritePath(obj.path, urlSpec.localized); if (path.error) { - return { error: `Path validation failed: ${path.error}` }; + return {error: `Path validation failed: ${path.error}`}; } - if (typeof obj.page !== "function") { - return { error: `Expected page to be function, got ${obj.content}` }; + if (typeof obj.page !== 'function') { + return {error: `Expected page to be function, got ${obj.content}`}; } break; } - case "data": { + case 'data': { const path = validateWritePath(obj.path, urlSpec.data); if (path.error) { - return { error: `Path validation failed: ${path.error}` }; + return {error: `Path validation failed: ${path.error}`}; } - if (typeof obj.data !== "function") { - return { error: `Expected data to be function, got ${obj.data}` }; + if (typeof obj.data !== 'function') { + return {error: `Expected data to be function, got ${obj.data}`}; } break; } - case "redirect": { + case 'redirect': { const fromPath = validateWritePath(obj.fromPath, urlSpec.localized); if (fromPath.error) { return { @@ -805,22 +804,22 @@ function validateWriteObject(obj) { const toPath = validateWritePath(obj.toPath, urlSpec.localized); if (toPath.error) { - return { error: `Path (toPath) validation failed: ${toPath.error}` }; + return {error: `Path (toPath) validation failed: ${toPath.error}`}; } - if (typeof obj.title !== "function") { - return { error: `Expected title to be function, got ${obj.title}` }; + if (typeof obj.title !== 'function') { + return {error: `Expected title to be function, got ${obj.title}`}; } break; } default: { - return { error: `Unknown type: ${obj.type}` }; + return {error: `Unknown type: ${obj.type}`}; } } - return { success: true }; + return {success: true}; } /* @@ -836,9 +835,9 @@ async function writeData(subKey, directory, data) { const writePage = {}; writePage.to = - ({ baseDirectory, pageSubKey, paths }) => + ({baseDirectory, pageSubKey, paths}) => (targetFullKey, ...args) => { - const [groupKey, subKey] = targetFullKey.split("."); + const [groupKey, subKey] = targetFullKey.split('.'); let path = paths.subdirectoryPrefix; let from; @@ -847,27 +846,27 @@ writePage.to = // When linking to *outside* the localized area of the site, we need to // make sure the result is correctly relative to the 8ase directory. if ( - groupKey !== "localized" && - groupKey !== "localizedDefaultLanguage" && + groupKey !== 'localized' && + groupKey !== 'localizedDefaultLanguage' && baseDirectory ) { - from = "localizedWithBaseDirectory." + pageSubKey; + from = 'localizedWithBaseDirectory.' + pageSubKey; to = targetFullKey; - } else if (groupKey === "localizedDefaultLanguage" && baseDirectory) { + } else if (groupKey === 'localizedDefaultLanguage' && baseDirectory) { // Special case for specifically linking *from* a page with base // directory *to* a page without! Used for the language switcher and // hopefully nothing else oh god. - from = "localizedWithBaseDirectory." + pageSubKey; - to = "localized." + subKey; - } else if (groupKey === "localizedDefaultLanguage") { + from = 'localizedWithBaseDirectory.' + pageSubKey; + to = 'localized.' + subKey; + } else if (groupKey === 'localizedDefaultLanguage') { // Linking to the default, except surprise, we're already IN the default // (no baseDirectory set). - from = "localized." + pageSubKey; - to = "localized." + subKey; + from = 'localized.' + pageSubKey; + to = 'localized.' + subKey; } else { // If we're linking inside the localized area (or there just is no // 8ase directory), the 8ase directory doesn't matter. - from = "localized." + pageSubKey; + from = 'localized.' + pageSubKey; to = targetFullKey; } @@ -890,13 +889,13 @@ writePage.html = ( wikiData, } ) => { - const { wikiInfo } = wikiData; + const {wikiInfo} = wikiData; let { - title = "", + title = '', meta = {}, - theme = "", - stylesheet = "", + theme = '', + stylesheet = '', showWikiNameInTitle = true, @@ -912,45 +911,45 @@ writePage.html = ( socialEmbed = {}, } = pageInfo; - body.style ??= ""; + body.style ??= ''; theme = theme || getThemeString(wikiInfo.color); banner ||= {}; banner.classes ??= []; - banner.src ??= ""; - banner.position ??= ""; + banner.src ??= ''; + banner.position ??= ''; banner.dimensions ??= [0, 0]; main.classes ??= []; - main.content ??= ""; + main.content ??= ''; sidebarLeft ??= {}; sidebarRight ??= {}; for (const sidebar of [sidebarLeft, sidebarRight]) { sidebar.classes ??= []; - sidebar.content ??= ""; + sidebar.content ??= ''; sidebar.collapse ??= true; } nav.classes ??= []; - nav.content ??= ""; - nav.bottomRowContent ??= ""; + nav.content ??= ''; + nav.bottomRowContent ??= ''; nav.links ??= []; nav.linkContainerClasses ??= []; secondaryNav ??= {}; - secondaryNav.content ??= ""; - secondaryNav.content ??= ""; + secondaryNav.content ??= ''; + secondaryNav.content ??= ''; footer.classes ??= []; footer.content ??= wikiInfo.footerContent ? transformMultiline(wikiInfo.footerContent) - : ""; + : ''; footer.content += - "\n" + + '\n' + getFooterLocalizationLinks(paths.pathname, { defaultLanguage, languages, @@ -960,13 +959,13 @@ writePage.html = ( }); const canonical = wikiInfo.canonicalBase - ? wikiInfo.canonicalBase + (paths.pathname === "/" ? "" : paths.pathname) - : ""; + ? wikiInfo.canonicalBase + (paths.pathname === '/' ? '' : paths.pathname) + : ''; const localizedCanonical = wikiInfo.canonicalBase - ? Object.entries(localizedPaths).map(([code, { pathname }]) => ({ + ? Object.entries(localizedPaths).map(([code, {pathname}]) => ({ lang: code, - href: wikiInfo.canonicalBase + (pathname === "/" ? "" : pathname), + href: wikiInfo.canonicalBase + (pathname === '/' ? '' : pathname), })) : []; @@ -976,9 +975,9 @@ writePage.html = ( const mainHTML = main.content && html.tag( - "main", + 'main', { - id: "content", + id: 'content', class: main.classes, }, main.content @@ -987,9 +986,9 @@ writePage.html = ( const footerHTML = footer.content && html.tag( - "footer", + 'footer', { - id: "footer", + id: 'footer', class: footer.classes, }, footer.content @@ -997,18 +996,18 @@ writePage.html = ( const generateSidebarHTML = ( id, - { content, multiple, classes, collapse = true, wide = false } + {content, multiple, classes, collapse = true, wide = false} ) => content ? html.tag( - "div", + 'div', { id, class: [ - "sidebar-column", - "sidebar", - wide && "wide", - !collapse && "no-hide", + 'sidebar-column', + 'sidebar', + wide && 'wide', + !collapse && 'no-hide', ...classes, ], }, @@ -1016,28 +1015,28 @@ writePage.html = ( ) : multiple ? html.tag( - "div", + 'div', { id, class: [ - "sidebar-column", - "sidebar-multiple", - wide && "wide", - !collapse && "no-hide", + 'sidebar-column', + 'sidebar-multiple', + wide && 'wide', + !collapse && 'no-hide', ], }, multiple.map((content) => - html.tag("div", { class: ["sidebar", ...classes] }, content) + html.tag('div', {class: ['sidebar', ...classes]}, content) ) ) - : ""; + : ''; - const sidebarLeftHTML = generateSidebarHTML("sidebar-left", sidebarLeft); - const sidebarRightHTML = generateSidebarHTML("sidebar-right", sidebarRight); + const sidebarLeftHTML = generateSidebarHTML('sidebar-left', sidebarLeft); + const sidebarRightHTML = generateSidebarHTML('sidebar-right', sidebarRight); if (nav.simple) { - nav.linkContainerClasses = ["nav-links-hierarchy"]; - nav.links = [{ toHome: true }, { toCurrentPage: true }]; + nav.linkContainerClasses = ['nav-links-hierarchy']; + nav.links = [{toHome: true}, {toCurrentPage: true}]; } const links = (nav.links || []).filter(Boolean); @@ -1048,7 +1047,7 @@ writePage.html = ( const prev = links[i - 1]; const next = links[i + 1]; - let { title: linkTitle } = cur; + let {title: linkTitle} = cur; if (cur.toHome) { linkTitle ??= wikiInfo.nameShort; @@ -1058,7 +1057,7 @@ writePage.html = ( let partContent; - if (typeof cur.html === "string") { + if (typeof cur.html === 'string') { if (!cur.html) { logWarn`Empty HTML in nav link ${JSON.stringify(cur)}`; console.trace(); @@ -1066,11 +1065,11 @@ writePage.html = ( partContent = cur.html; } else { const attributes = { - class: (cur.toCurrentPage || i === links.length - 1) && "current", + class: (cur.toCurrentPage || i === links.length - 1) && 'current', href: cur.toCurrentPage - ? "" + ? '' : cur.toHome - ? to("localized.home") + ? to('localized.home') : cur.path ? to(...cur.path) : cur.href @@ -1087,12 +1086,12 @@ writePage.html = ( )})` ); } - partContent = html.tag("a", attributes, linkTitle); + partContent = html.tag('a', attributes, linkTitle); } const part = html.tag( - "span", - { class: cur.divider === false && "no-divider" }, + 'span', + {class: cur.divider === false && 'no-divider'}, partContent ); @@ -1100,35 +1099,35 @@ writePage.html = ( } const navHTML = html.tag( - "nav", + 'nav', { [html.onlyIfContent]: true, - id: "header", + id: 'header', class: [ ...nav.classes, - links.length && "nav-has-main-links", - nav.content && "nav-has-content", - nav.bottomRowContent && "nav-has-bottom-row", + links.length && 'nav-has-main-links', + nav.content && 'nav-has-content', + nav.bottomRowContent && 'nav-has-bottom-row', ], }, [ links.length && html.tag( - "div", - { class: ["nav-main-links", ...nav.linkContainerClasses] }, + 'div', + {class: ['nav-main-links', ...nav.linkContainerClasses]}, navLinkParts ), - nav.content && html.tag("div", { class: "nav-content" }, nav.content), + nav.content && html.tag('div', {class: 'nav-content'}, nav.content), nav.bottomRowContent && - html.tag("div", { class: "nav-bottom-row" }, nav.bottomRowContent), + html.tag('div', {class: 'nav-bottom-row'}, nav.bottomRowContent), ] ); const secondaryNavHTML = html.tag( - "nav", + 'nav', { [html.onlyIfContent]: true, - id: "secondary-nav", + id: 'secondary-nav', class: secondaryNav.classes, }, [secondaryNav.content] @@ -1144,12 +1143,12 @@ writePage.html = ( banner.position && bannerSrc && html.tag( - "div", + 'div', { - id: "banner", + id: 'banner', class: banner.classes, }, - html.tag("img", { + html.tag('img', { src: bannerSrc, alt: banner.alt, width: banner.dimensions[0] || 1100, @@ -1159,18 +1158,18 @@ writePage.html = ( const layoutHTML = [ navHTML, - banner.position === "top" && bannerHTML, + banner.position === 'top' && bannerHTML, secondaryNavHTML, html.tag( - "div", - { class: ["layout-columns", !collapseSidebars && "vertical-when-thin"] }, + 'div', + {class: ['layout-columns', !collapseSidebars && 'vertical-when-thin']}, [sidebarLeftHTML, mainHTML, sidebarRightHTML] ), - banner.position === "bottom" && bannerHTML, + banner.position === 'bottom' && bannerHTML, footerHTML, ] .filter(Boolean) - .join("\n"); + .join('\n'); const infoCardHTML = fixWS`
    @@ -1178,36 +1177,36 @@ writePage.html = (
    ${img({ - class: "info-card-art", - src: "", + class: 'info-card-art', + src: '', link: true, square: true, })}
    ${img({ - class: "info-card-art", - src: "", + class: 'info-card-art', + src: '', link: true, square: true, reveal: getRevealStringFromWarnings( '', - { language } + {language} ), })}

    ${language.$( - "releaseInfo.from", - { album: "" } + 'releaseInfo.from', + {album: ''} )}

    ${language.$( - "releaseInfo.by", - { artists: "" } + 'releaseInfo.by', + {artists: ''} )}

    ${language.$( - "releaseInfo.coverArtBy", - { artists: "" } + 'releaseInfo.coverArtBy', + {artists: ''} )}

    @@ -1216,47 +1215,47 @@ writePage.html = ( const socialEmbedHTML = [ socialEmbed.title && - html.tag("meta", { property: "og:title", content: socialEmbed.title }), + html.tag('meta', {property: 'og:title', content: socialEmbed.title}), socialEmbed.description && - html.tag("meta", { - property: "og:description", + html.tag('meta', { + property: 'og:description', content: socialEmbed.description, }), socialEmbed.image && - html.tag("meta", { property: "og:image", content: socialEmbed.image }), + html.tag('meta', {property: 'og:image', content: socialEmbed.image}), socialEmbed.color && - html.tag("meta", { name: "theme-color", content: socialEmbed.color }), + html.tag('meta', {name: 'theme-color', content: socialEmbed.color}), oEmbedJSONHref && - html.tag("link", { - type: "application/json+oembed", + html.tag('link', { + type: 'application/json+oembed', href: oEmbedJSONHref, }), ] .filter(Boolean) - .join("\n"); + .join('\n'); return filterEmptyLines(fixWS` [["data-url-value" + i], v]) + paths.toPath.slice(1).map((v, i) => [['data-url-value' + i], v]) ), - "data-rebase-localized": to("localized.root"), - "data-rebase-shared": to("shared.root"), - "data-rebase-media": to("media.root"), - "data-rebase-data": to("data.root"), + 'data-rebase-localized': to('localized.root'), + 'data-rebase-shared': to('shared.root'), + 'data-rebase-media': to('media.root'), + 'data-rebase-data': to('data.root'), })}> ${ showWikiNameInTitle - ? language.formatString("misc.pageTitle.withWikiName", { + ? language.formatString('misc.pageTitle.withWikiName', { title, wikiName: wikiInfo.nameShort, }) - : language.formatString("misc.pageTitle", { title }) + : language.formatString('misc.pageTitle', {title}) } @@ -1266,17 +1265,17 @@ writePage.html = ( ([key, value]) => `` ) - .join("\n")} + .join('\n')} ${canonical && ``} ${localizedCanonical .map( - ({ lang, href }) => + ({lang, href}) => `` ) - .join("\n")} + .join('\n')} ${socialEmbedHTML} ${ @@ -1289,11 +1288,11 @@ writePage.html = ( ` } - +
    ${ mainHTML && @@ -1301,28 +1300,28 @@ writePage.html = (
    ${[ [ - "#content", - language.$("misc.skippers.skipToContent"), + '#content', + language.$('misc.skippers.skipToContent'), ], sidebarLeftHTML && [ - "#sidebar-left", + '#sidebar-left', sidebarRightHTML ? language.$( - "misc.skippers.skipToSidebar.left" + 'misc.skippers.skipToSidebar.left' ) - : language.$("misc.skippers.skipToSidebar"), + : language.$('misc.skippers.skipToSidebar'), ], sidebarRightHTML && [ - "#sidebar-right", + '#sidebar-right', sidebarLeftHTML ? language.$( - "misc.skippers.skipToSidebar.right" + 'misc.skippers.skipToSidebar.right' ) - : language.$("misc.skippers.skipToSidebar"), + : language.$('misc.skippers.skipToSidebar'), ], footerHTML && [ - "#footer", - language.$("misc.skippers.skipToFooter"), + '#footer', + language.$('misc.skippers.skipToFooter'), ], ] .filter(Boolean) @@ -1331,7 +1330,7 @@ writePage.html = ( ${title} ` ) - .join("\n")} + .join('\n')}
    ` } @@ -1339,7 +1338,7 @@ writePage.html = (
    ${infoCardHTML} @@ -1347,37 +1346,37 @@ writePage.html = ( `); }; -writePage.oEmbedJSON = (pageInfo, { language, wikiData }) => { - const { socialEmbed } = pageInfo; - const { wikiInfo } = wikiData; - const { canonicalBase, nameShort } = wikiInfo; +writePage.oEmbedJSON = (pageInfo, {language, wikiData}) => { + const {socialEmbed} = pageInfo; + const {wikiInfo} = wikiData; + const {canonicalBase, nameShort} = wikiInfo; - if (!socialEmbed) return ""; + if (!socialEmbed) return ''; const entries = [ socialEmbed.heading && [ - "author_name", - language.$("misc.socialEmbed.heading", { + 'author_name', + language.$('misc.socialEmbed.heading', { wikiName: nameShort, heading: socialEmbed.heading, }), ], socialEmbed.headingLink && canonicalBase && [ - "author_url", - canonicalBase.replace(/\/$/, "") + - "/" + - socialEmbed.headingLink.replace(/^\//, ""), + 'author_url', + canonicalBase.replace(/\/$/, '') + + '/' + + socialEmbed.headingLink.replace(/^\//, ''), ], ].filter(Boolean); - if (!entries.length) return ""; + if (!entries.length) return ''; return JSON.stringify(Object.fromEntries(entries)); }; -writePage.write = async ({ html, oEmbedJSON = "", paths }) => { - await mkdir(paths.outputDirectory, { recursive: true }); +writePage.write = async ({html, oEmbedJSON = '', paths}) => { + await mkdir(paths.outputDirectory, {recursive: true}); await Promise.all( [ writeFile(paths.outputFile, html), @@ -1390,25 +1389,25 @@ writePage.write = async ({ html, oEmbedJSON = "", paths }) => { writePage.paths = ( baseDirectory, fullKey, - directory = "", - { file = "index.html" } = {} + directory = '', + {file = 'index.html'} = {} ) => { - const [groupKey, subKey] = fullKey.split("."); + const [groupKey, subKey] = fullKey.split('.'); const pathname = - groupKey === "localized" && baseDirectory + groupKey === 'localized' && baseDirectory ? urls - .from("shared.root") + .from('shared.root') .toDevice( - "localizedWithBaseDirectory." + subKey, + 'localizedWithBaseDirectory.' + subKey, baseDirectory, directory ) - : urls.from("shared.root").toDevice(fullKey, directory); + : urls.from('shared.root').toDevice(fullKey, directory); // Needed for the rare directory which itself contains a slash, e.g. for // listings, with directories like 'albums/by-name'. - const subdirectoryPrefix = "../".repeat(directory.split("/").length - 1); + const subdirectoryPrefix = '../'.repeat(directory.split('/').length - 1); const outputDirectory = path.join(outputPath, pathname); const outputFile = path.join(outputDirectory, file); @@ -1445,74 +1444,74 @@ async function writeFavicon() { } function writeSymlinks() { - return progressPromiseAll("Writing site symlinks.", [ - link(path.join(__dirname, UTILITY_DIRECTORY), "shared.utilityRoot"), - link(path.join(__dirname, STATIC_DIRECTORY), "shared.staticRoot"), - link(mediaPath, "media.root"), + return progressPromiseAll('Writing site symlinks.', [ + link(path.join(__dirname, UTILITY_DIRECTORY), 'shared.utilityRoot'), + link(path.join(__dirname, STATIC_DIRECTORY), 'shared.staticRoot'), + link(mediaPath, 'media.root'), ]); async function link(directory, urlKey) { - const pathname = urls.from("shared.root").toDevice(urlKey); + const pathname = urls.from('shared.root').toDevice(urlKey); const file = path.join(outputPath, pathname); try { await unlink(file); } catch (error) { - if (error.code !== "ENOENT") { + if (error.code !== 'ENOENT') { throw error; } } try { await symlink(path.resolve(directory), file); } catch (error) { - if (error.code === "EPERM") { - await symlink(path.resolve(directory), file, "junction"); + if (error.code === 'EPERM') { + await symlink(path.resolve(directory), file, 'junction'); } } } } -function writeSharedFilesAndPages({ language, wikiData }) { - const { groupData, wikiInfo } = wikiData; +function writeSharedFilesAndPages({language, wikiData}) { + const {groupData, wikiInfo} = wikiData; const redirect = async (title, from, urlKey, directory) => { const target = path.relative( from, - urls.from("shared.root").to(urlKey, directory) + urls.from('shared.root').to(urlKey, directory) ); - const content = generateRedirectPage(title, target, { language }); - await mkdir(path.join(outputPath, from), { recursive: true }); - await writeFile(path.join(outputPath, from, "index.html"), content); + const content = generateRedirectPage(title, target, {language}); + await mkdir(path.join(outputPath, from), {recursive: true}); + await writeFile(path.join(outputPath, from, 'index.html'), content); }; return progressPromiseAll( `Writing files & pages shared across languages.`, [ - groupData?.some((group) => group.directory === "fandom") && + groupData?.some((group) => group.directory === 'fandom') && redirect( - "Fandom - Gallery", - "albums/fandom", - "localized.groupGallery", - "fandom" + 'Fandom - Gallery', + 'albums/fandom', + 'localized.groupGallery', + 'fandom' ), - groupData?.some((group) => group.directory === "official") && + groupData?.some((group) => group.directory === 'official') && redirect( - "Official - Gallery", - "albums/official", - "localized.groupGallery", - "official" + 'Official - Gallery', + 'albums/official', + 'localized.groupGallery', + 'official' ), wikiInfo.enableListings && redirect( - "Album Commentary", - "list/all-commentary", - "localized.commentaryIndex", - "" + 'Album Commentary', + 'list/all-commentary', + 'localized.commentaryIndex', + '' ), writeFile( - path.join(outputPath, "data.json"), + path.join(outputPath, 'data.json'), fixWS` { "albumData": ${stringifyThings(wikiData.albumData)}, @@ -1528,12 +1527,12 @@ function writeSharedFilesAndPages({ language, wikiData }) { ); } -function generateRedirectPage(title, target, { language }) { +function generateRedirectPage(title, target, {language}) { return fixWS` - ${language.$("redirectPage.title", { title })} + ${language.$('redirectPage.title', {title})} @@ -1541,8 +1540,8 @@ function generateRedirectPage(title, target, { language }) {
    -

    ${language.$("redirectPage.title", { title })}

    -

    ${language.$("redirectPage.infoLine", { +

    ${language.$('redirectPage.title', {title})}

    +

    ${language.$('redirectPage.infoLine', { target: `${target}`, })}

    @@ -1553,41 +1552,41 @@ function generateRedirectPage(title, target, { language }) { // RIP toAnythingMan (previously getHrefOfAnythingMan), 2020-05-25<>2021-05-14. // ........Yet the function 8reathes life anew as linkAnythingMan! ::::) -function linkAnythingMan(anythingMan, { link, wikiData, ...opts }) { +function linkAnythingMan(anythingMan, {link, wikiData, ...opts}) { return wikiData.albumData.includes(anythingMan) ? link.album(anythingMan, opts) : wikiData.trackData.includes(anythingMan) ? link.track(anythingMan, opts) : wikiData.flashData?.includes(anythingMan) ? link.flash(anythingMan, opts) - : "idk bud"; + : 'idk bud'; } async function processLanguageFile(file) { - const contents = await readFile(file, "utf-8"); + const contents = await readFile(file, 'utf-8'); const json = JSON.parse(contents); - const code = json["meta.languageCode"]; + const code = json['meta.languageCode']; if (!code) { throw new Error(`Missing language code (file: ${file})`); } - delete json["meta.languageCode"]; + delete json['meta.languageCode']; - const intlCode = json["meta.languageIntlCode"] ?? null; - delete json["meta.languageIntlCode"]; + const intlCode = json['meta.languageIntlCode'] ?? null; + delete json['meta.languageIntlCode']; - const name = json["meta.languageName"]; + const name = json['meta.languageName']; if (!name) { throw new Error(`Missing language name (${code})`); } - delete json["meta.languageName"]; + delete json['meta.languageName']; - const hidden = json["meta.hidden"] ?? false; - delete json["meta.hidden"]; + const hidden = json['meta.hidden'] ?? false; + delete json['meta.hidden']; - if (json["meta.baseDirectory"]) { + if (json['meta.baseDirectory']) { logWarn`(${code}) Language JSON still has unused meta.baseDirectory`; - delete json["meta.baseDirectory"]; + delete json['meta.baseDirectory']; } const language = new Language(); @@ -1596,18 +1595,18 @@ async function processLanguageFile(file) { language.name = name; language.hidden = hidden; language.escapeHTML = (string) => - he.encode(string, { useNamedReferences: true }); + he.encode(string, {useNamedReferences: true}); language.strings = json; return language; } // Wrapper function for running a function once for all languages. -async function wrapLanguages(fn, { languages, writeOneLanguage = null }) { +async function wrapLanguages(fn, {languages, writeOneLanguage = null}) { const k = writeOneLanguage; - const languagesToRun = k ? { [k]: languages[k] } : languages; + const languagesToRun = k ? {[k]: languages[k]} : languages; const entries = Object.entries(languagesToRun).filter( - ([key]) => key !== "default" + ([key]) => key !== 'default' ); for (let i = 0; i < entries.length; i++) { @@ -1629,15 +1628,15 @@ async function main() { // Data files for the site, including flash, artist, and al8um data, // and like a jillion other things too. Pretty much everything which // makes an individual wiki what it is goes here! - "data-path": { - type: "value", + 'data-path': { + type: 'value', }, // Static media will 8e referenced in the site here! The contents are // categorized; check out MEDIA_ALBUM_ART_DIRECTORY and other constants // near the top of this file (upd8.js). - "media-path": { - type: "value", + 'media-path': { + type: 'value', }, // String files! For the most part, this is used for translating the @@ -1650,8 +1649,8 @@ async function main() { // Unlike the other options here, this one's optional - the site will // 8uild with the default (English) strings if this path is left // unspecified. - "lang-path": { - type: "value", + 'lang-path': { + type: 'value', }, // This is the output directory. It's the one you'll upload online with @@ -1660,78 +1659,78 @@ async function main() { // site. Just keep in mind that the gener8ted result will contain a // couple symlinked directories, so if you're uploading, you're pro8a8ly // gonna want to resolve those yourself. - "out-path": { - type: "value", + 'out-path': { + type: 'value', }, // Thum8nail gener8tion is *usually* something you want, 8ut it can 8e // kinda a pain to run every time, since it does necessit8te reading // every media file at run time. Pass this to skip it. - "skip-thumbs": { - type: "flag", + 'skip-thumbs': { + type: 'flag', }, // Or, if you *only* want to gener8te newly upd8ted thum8nails, you can // pass this flag! It exits 8efore 8uilding the rest of the site. - "thumbs-only": { - type: "flag", + 'thumbs-only': { + type: 'flag', }, // Just working on data entries and not interested in actually // generating site HTML yet? This flag will cut execution off right // 8efore any site 8uilding actually happens. - "no-build": { - type: "flag", + 'no-build': { + type: 'flag', }, // Only want to 8uild one language during testing? This can chop down // 8uild times a pretty 8ig chunk! Just pass a single language code. lang: { - type: "value", + type: 'value', }, // Working without a dev server and just using file:// URLs in your we8 // 8rowser? This will automatically append index.html to links across // the site. Not recommended for production, since it isn't guaranteed // 100% error-free (and index.html-style links are less pretty anyway). - "append-index-html": { - type: "flag", + 'append-index-html': { + type: 'flag', }, // Want sweet, sweet trace8ack info in aggreg8te error messages? This // will print all the juicy details (or at least the first relevant // line) right to your output, 8ut also pro8a8ly give you a headache // 8ecause wow that is a lot of visual noise. - "show-traces": { - type: "flag", + 'show-traces': { + type: 'flag', }, - "queue-size": { - type: "value", + 'queue-size': { + type: 'value', validate(size) { - if (parseInt(size) !== parseFloat(size)) return "an integer"; - if (parseInt(size) < 0) return "a counting number or zero"; + if (parseInt(size) !== parseFloat(size)) return 'an integer'; + if (parseInt(size) < 0) return 'a counting number or zero'; return true; }, }, - queue: { alias: "queue-size" }, + queue: {alias: 'queue-size'}, // This option is super slow and has the potential for bugs! It puts // CacheableObject in a mode where every instance is a Proxy which will // keep track of invalid property accesses. - "show-invalid-property-accesses": { - type: "flag", + 'show-invalid-property-accesses': { + type: 'flag', }, [parseOptions.handleUnknown]: () => {}, }); - dataPath = miscOptions["data-path"] || process.env.HSMUSIC_DATA; - mediaPath = miscOptions["media-path"] || process.env.HSMUSIC_MEDIA; - langPath = miscOptions["lang-path"] || process.env.HSMUSIC_LANG; // Can 8e left unset! - outputPath = miscOptions["out-path"] || process.env.HSMUSIC_OUT; + dataPath = miscOptions['data-path'] || process.env.HSMUSIC_DATA; + mediaPath = miscOptions['media-path'] || process.env.HSMUSIC_MEDIA; + langPath = miscOptions['lang-path'] || process.env.HSMUSIC_LANG; // Can 8e left unset! + outputPath = miscOptions['out-path'] || process.env.HSMUSIC_OUT; - const writeOneLanguage = miscOptions["lang"]; + const writeOneLanguage = miscOptions['lang']; { let errored = false; @@ -1752,16 +1751,16 @@ async function main() { } } - const appendIndexHTML = miscOptions["append-index-html"] ?? false; + const appendIndexHTML = miscOptions['append-index-html'] ?? false; if (appendIndexHTML) { logWarn`Appending index.html to link hrefs. (Note: not recommended for production release!)`; unbound_link.globalOptions.appendIndexHTML = true; } - const skipThumbs = miscOptions["skip-thumbs"] ?? false; - const thumbsOnly = miscOptions["thumbs-only"] ?? false; - const noBuild = miscOptions["no-build"] ?? false; - const showAggregateTraces = miscOptions["show-traces"] ?? false; + const skipThumbs = miscOptions['skip-thumbs'] ?? false; + const thumbsOnly = miscOptions['thumbs-only'] ?? false; + const noBuild = miscOptions['no-build'] ?? false; + const showAggregateTraces = miscOptions['show-traces'] ?? false; const niceShowAggregate = (error, ...opts) => { showAggregate(error, { @@ -1780,45 +1779,45 @@ async function main() { logInfo`Skipping thumbnail generation.`; } else { logInfo`Begin thumbnail generation... -----+`; - const result = await genThumbs(mediaPath, { queueSize, quiet: true }); + const result = await genThumbs(mediaPath, {queueSize, quiet: true}); logInfo`Done thumbnail generation! --------+`; if (!result) return; if (thumbsOnly) return; } const showInvalidPropertyAccesses = - miscOptions["show-invalid-property-accesses"] ?? false; + miscOptions['show-invalid-property-accesses'] ?? false; if (showInvalidPropertyAccesses) { CacheableObject.DEBUG_SLOW_TRACK_INVALID_PROPERTIES = true; } - const { aggregate: processDataAggregate, result: wikiDataResult } = - await loadAndProcessDataDocuments({ dataPath }); + const {aggregate: processDataAggregate, result: wikiDataResult} = + await loadAndProcessDataDocuments({dataPath}); Object.assign(wikiData, wikiDataResult); { const logThings = (thingDataProp, label) => logInfo` - ${ - wikiData[thingDataProp]?.length ?? color.red("(Missing!)") + wikiData[thingDataProp]?.length ?? color.red('(Missing!)') } ${color.normal(color.dim(label))}`; try { logInfo`Loaded data and processed objects:`; - logThings("albumData", "albums"); - logThings("trackData", "tracks"); - logThings("artistData", "artists"); + logThings('albumData', 'albums'); + logThings('trackData', 'tracks'); + logThings('artistData', 'artists'); if (wikiData.flashData) { - logThings("flashData", "flashes"); - logThings("flashActData", "flash acts"); + logThings('flashData', 'flashes'); + logThings('flashActData', 'flash acts'); } - logThings("groupData", "groups"); - logThings("groupCategoryData", "group categories"); - logThings("artTagData", "art tags"); + logThings('groupData', 'groups'); + logThings('groupCategoryData', 'group categories'); + logThings('artTagData', 'art tags'); if (wikiData.newsData) { - logThings("newsData", "news entries"); + logThings('newsData', 'news entries'); } - logThings("staticPageData", "static pages"); + logThings('staticPageData', 'static pages'); if (wikiData.homepageLayout) { logInfo` - ${1} homepage layout (${ wikiData.homepageLayout.rows.length @@ -1925,7 +1924,7 @@ async function main() { let languages; if (langPath) { const languageDataFiles = await findFiles(langPath, { - filter: (f) => path.extname(f) === ".json", + filter: (f) => path.extname(f) === '.json', }); const results = await progressPromiseAll( @@ -1953,7 +1952,7 @@ async function main() { if (langPath) { logError`Check if an appropriate file exists in ${langPath}?`; } else { - logError`Be sure to specify ${"--lang"} or ${"HSMUSIC_LANG"} with the path to language files.`; + logError`Be sure to specify ${'--lang'} or ${'HSMUSIC_LANG'} with the path to language files.`; } return; } else { @@ -1969,7 +1968,7 @@ async function main() { language.inheritedStrings = finalDefaultLanguage.strings; } - logInfo`Loaded language strings: ${Object.keys(languages).join(", ")}`; + logInfo`Loaded language strings: ${Object.keys(languages).join(', ')}`; if (noBuild) { logInfo`Not generating any site or page files this run (--no-build passed).`; @@ -2030,19 +2029,19 @@ async function main() { device: path.join( mediaPath, urls - .from("media.root") - .toDevice("media.albumAdditionalFile", album.directory, file) + .from('media.root') + .toDevice('media.albumAdditionalFile', album.directory, file) ), media: urls - .from("media.root") - .to("media.albumAdditionalFile", album.directory, file), + .from('media.root') + .to('media.albumAdditionalFile', album.directory, file), })) ), ]; const getSizeOfAdditionalFile = (mediaPath) => { - const { device = null } = - additionalFilePaths.find(({ media }) => media === mediaPath) || {}; + const {device = null} = + additionalFilePaths.find(({media}) => media === mediaPath) || {}; if (!device) return null; return fileSizePreloader.getSizeOfPath(device); }; @@ -2062,7 +2061,7 @@ async function main() { // performance right now 'cuz it'll w8 for file writes to 8e completed // 8efore moving on to more data processing. So, defaults to zero, which // disa8les the queue feature altogether. - queueSize = +(miscOptions["queue-size"] ?? 0); + queueSize = +(miscOptions['queue-size'] ?? 0); const buildDictionary = pageSpecs; @@ -2072,11 +2071,11 @@ async function main() { // on some particular area(s) of the site rather than making changes // across all of them. const writeFlags = await parseOptions(process.argv.slice(2), { - all: { type: "flag" }, // Defaults to true if none 8elow specified. + all: {type: 'flag'}, // Defaults to true if none 8elow specified. // Kinda a hack t8h! ...Object.fromEntries( - Object.keys(buildDictionary).map((key) => [key, { type: "flag" }]) + Object.keys(buildDictionary).map((key) => [key, {type: 'flag'}]) ), [parseOptions.handleUnknown]: () => {}, @@ -2085,12 +2084,12 @@ async function main() { const writeAll = !Object.keys(writeFlags).length || writeFlags.all; logInfo`Writing site pages: ${ - writeAll ? "all" : Object.keys(writeFlags).join(", ") + writeAll ? 'all' : Object.keys(writeFlags).join(', ') }`; await writeFavicon(); await writeSymlinks(); - await writeSharedFilesAndPages({ language: finalDefaultLanguage, wikiData }); + await writeSharedFilesAndPages({language: finalDefaultLanguage, wikiData}); const buildSteps = writeAll ? Object.entries(buildDictionary) @@ -2103,33 +2102,33 @@ async function main() { const buildStepsWithTargets = buildSteps .map(([flag, pageSpec]) => { // Condition not met: skip this build step altogether. - if (pageSpec.condition && !pageSpec.condition({ wikiData })) { + if (pageSpec.condition && !pageSpec.condition({wikiData})) { return null; } // May still call writeTargetless if present. if (!pageSpec.targets) { - return { flag, pageSpec, targets: [] }; + return {flag, pageSpec, targets: []}; } if (!pageSpec.write) { - logError`${flag + ".targets"} is specified, but ${ - flag + ".write" + logError`${flag + '.targets'} is specified, but ${ + flag + '.write' } is missing!`; error = true; return null; } - const targets = pageSpec.targets({ wikiData }); + const targets = pageSpec.targets({wikiData}); if (!Array.isArray(targets)) { logError`${ - flag + ".targets" + flag + '.targets' } was called, but it didn't return an array! (${typeof targets})`; error = true; return null; } - return { flag, pageSpec, targets }; + return {flag, pageSpec, targets}; }) .filter(Boolean); @@ -2149,7 +2148,7 @@ async function main() { if ( !( - writes.every((obj) => typeof obj === "object") && + writes.every((obj) => typeof obj === 'object') && writes.every((obj) => { const result = validateWriteObject(obj); if (result.error) { @@ -2171,19 +2170,19 @@ async function main() { // return; - writes = buildStepsWithTargets.flatMap(({ flag, pageSpec, targets }) => { + writes = buildStepsWithTargets.flatMap(({flag, pageSpec, targets}) => { const writes = targets.flatMap( - (target) => pageSpec.write(target, { wikiData })?.slice() || [] + (target) => pageSpec.write(target, {wikiData})?.slice() || [] ); - if (!validateWrites(writes, flag + ".write")) { + if (!validateWrites(writes, flag + '.write')) { return []; } if (pageSpec.writeTargetless) { - const writes2 = pageSpec.writeTargetless({ wikiData }); + const writes2 = pageSpec.writeTargetless({wikiData}); - if (!validateWrites(writes2, flag + ".writeTargetless")) { + if (!validateWrites(writes2, flag + '.writeTargetless')) { return []; } @@ -2198,9 +2197,9 @@ async function main() { } } - const pageWrites = writes.filter(({ type }) => type === "page"); - const dataWrites = writes.filter(({ type }) => type === "data"); - const redirectWrites = writes.filter(({ type }) => type === "redirect"); + const pageWrites = writes.filter(({type}) => type === 'page'); + const dataWrites = writes.filter(({type}) => type === 'data'); + const redirectWrites = writes.filter(({type}) => type === 'redirect'); if (writes.length) { logInfo`Total of ${writes.length} writes returned. (${pageWrites.length} page, ${dataWrites.length} data [currently skipped], ${redirectWrites.length} redirect)`; @@ -2247,20 +2246,20 @@ async function main() { const perLanguageFn = async (language, i, entries) => { const baseDirectory = - language === finalDefaultLanguage ? "" : language.code; + language === finalDefaultLanguage ? '' : language.code; console.log( `\x1b[34;1m${`[${i + 1}/${entries.length}] ${ language.code - } (-> /${baseDirectory}) `.padEnd(60, "-")}\x1b[0m` + } (-> /${baseDirectory}) `.padEnd(60, '-')}\x1b[0m` ); await progressPromiseAll( `Writing ${language.code}`, queue( [ - ...pageWrites.map(({ type, ...props }) => () => { - const { path, page } = props; + ...pageWrites.map(({type, ...props}) => () => { + const {path, page} = props; // TODO: This only supports one <>-style argument. const pageSubKey = path[0]; @@ -2269,13 +2268,13 @@ async function main() { const localizedPaths = Object.fromEntries( Object.entries(languages) .filter( - ([key, language]) => key !== "default" && !language.hidden + ([key, language]) => key !== 'default' && !language.hidden ) .map(([key, language]) => [ language.code, writePage.paths( - language === finalDefaultLanguage ? "" : language.code, - "localized." + pageSubKey, + language === finalDefaultLanguage ? '' : language.code, + 'localized.' + pageSubKey, directory ), ]) @@ -2283,7 +2282,7 @@ async function main() { const paths = writePage.paths( baseDirectory, - "localized." + pageSubKey, + 'localized.' + pageSubKey, directory ); @@ -2294,13 +2293,13 @@ async function main() { }); const absoluteTo = (targetFullKey, ...args) => { - const [groupKey, subKey] = targetFullKey.split("."); - const from = urls.from("shared.root"); + const [groupKey, subKey] = targetFullKey.split('.'); + const from = urls.from('shared.root'); return ( - "/" + - (groupKey === "localized" && baseDirectory + '/' + + (groupKey === 'localized' && baseDirectory ? from.to( - "localizedWithBaseDirectory." + subKey, + 'localizedWithBaseDirectory.' + subKey, baseDirectory, ...args ) @@ -2314,7 +2313,7 @@ async function main() { const bound = {}; bound.link = withEntries(unbound_link, (entries) => - entries.map(([key, fn]) => [key, bindOpts(fn, { to })]) + entries.map(([key, fn]) => [key, bindOpts(fn, {to})]) ); bound.linkAnythingMan = bindOpts(linkAnythingMan, { @@ -2326,7 +2325,7 @@ async function main() { to, }); - bound.find = bindFind(wikiData, { mode: "warn" }); + bound.find = bindFind(wikiData, {mode: 'warn'}); bound.transformInline = bindOpts(transformInline, { find: bound.find, @@ -2501,8 +2500,8 @@ async function main() { wikiData.wikiInfo.canonicalBase && wikiData.wikiInfo.canonicalBase + urls - .from("shared.root") - .to("shared.path", paths.pathname + OEMBED_JSON_FILE); + .from('shared.root') + .to('shared.path', paths.pathname + OEMBED_JSON_FILE); const html = writePage.html(pageInfo, { defaultLanguage: finalDefaultLanguage, @@ -2522,30 +2521,27 @@ async function main() { paths, }); }), - ...redirectWrites.map( - ({ fromPath, toPath, title: titleFn }) => - () => { - const title = titleFn({ - language, - }); - - // TODO: This only supports one <>-style argument. - const fromPaths = writePage.paths( - baseDirectory, - "localized." + fromPath[0], - fromPath[1] - ); - const to = writePage.to({ - baseDirectory, - pageSubKey: fromPath[0], - paths: fromPaths, - }); - - const target = to("localized." + toPath[0], ...toPath.slice(1)); - const html = generateRedirectPage(title, target, { language }); - return writePage.write({ html, paths: fromPaths }); - } - ), + ...redirectWrites.map(({fromPath, toPath, title: titleFn}) => () => { + const title = titleFn({ + language, + }); + + // TODO: This only supports one <>-style argument. + const fromPaths = writePage.paths( + baseDirectory, + 'localized.' + fromPath[0], + fromPath[1] + ); + const to = writePage.to({ + baseDirectory, + pageSubKey: fromPath[0], + paths: fromPaths, + }); + + const target = to('localized.' + toPath[0], ...toPath.slice(1)); + const html = generateRedirectPage(title, target, {language}); + return writePage.write({html, paths: fromPaths}); + }), ], queueSize ) diff --git a/src/url-spec.js b/src/url-spec.js index 0defced..bab97ef 100644 --- a/src/url-spec.js +++ b/src/url-spec.js @@ -1,18 +1,18 @@ -// @format +/** @format */ -import { withEntries } from "./util/sugar.js"; +import {withEntries} from './util/sugar.js'; const urlSpec = { data: { - prefix: "data/", + prefix: 'data/', paths: { - root: "", - path: "<>", + root: '', + path: '<>', - album: "album/<>", - artist: "artist/<>", - track: "track/<>", + album: 'album/<>', + artist: 'artist/<>', + track: 'track/<>', }, }, @@ -21,64 +21,64 @@ const urlSpec = { // prefix: '_languageCode', paths: { - root: "", - path: "<>", + root: '', + path: '<>', - home: "", + home: '', - album: "album/<>/", - albumCommentary: "commentary/album/<>/", + album: 'album/<>/', + albumCommentary: 'commentary/album/<>/', - artist: "artist/<>/", - artistGallery: "artist/<>/gallery/", + artist: 'artist/<>/', + artistGallery: 'artist/<>/gallery/', - commentaryIndex: "commentary/", + commentaryIndex: 'commentary/', - flashIndex: "flash/", - flash: "flash/<>/", + flashIndex: 'flash/', + flash: 'flash/<>/', - groupInfo: "group/<>/", - groupGallery: "group/<>/gallery/", + groupInfo: 'group/<>/', + groupGallery: 'group/<>/gallery/', - listingIndex: "list/", - listing: "list/<>/", + listingIndex: 'list/', + listing: 'list/<>/', - newsIndex: "news/", - newsEntry: "news/<>/", + newsIndex: 'news/', + newsEntry: 'news/<>/', - staticPage: "<>/", - tag: "tag/<>/", - track: "track/<>/", + staticPage: '<>/', + tag: 'tag/<>/', + track: 'track/<>/', }, }, shared: { paths: { - root: "", - path: "<>", + root: '', + path: '<>', - utilityRoot: "util", - staticRoot: "static", + utilityRoot: 'util', + staticRoot: 'static', - utilityFile: "util/<>", - staticFile: "static/<>", + utilityFile: 'util/<>', + staticFile: 'static/<>', }, }, media: { - prefix: "media/", + prefix: 'media/', paths: { - root: "", - path: "<>", - - albumCover: "album-art/<>/cover.<>", - albumWallpaper: "album-art/<>/bg.<>", - albumBanner: "album-art/<>/banner.<>", - trackCover: "album-art/<>/<>.<>", - artistAvatar: "artist-avatar/<>.<>", - flashArt: "flash-art/<>.<>", - albumAdditionalFile: "album-additional/<>/<>", + root: '', + path: '<>', + + albumCover: 'album-art/<>/cover.<>', + albumWallpaper: 'album-art/<>/bg.<>', + albumBanner: 'album-art/<>/banner.<>', + trackCover: 'album-art/<>/<>.<>', + artistAvatar: 'artist-avatar/<>.<>', + flashArt: 'flash-art/<>.<>', + albumAdditionalFile: 'album-additional/<>/<>', }, }, }; @@ -87,7 +87,7 @@ const urlSpec = { // so it should never be referenced manually. urlSpec.localizedWithBaseDirectory = { paths: withEntries(urlSpec.localized.paths, (entries) => - entries.map(([key, path]) => [key, "<>/" + path]) + entries.map(([key, path]) => [key, '<>/' + path]) ), }; diff --git a/src/util/cli.js b/src/util/cli.js index 159d526..d28ef40 100644 --- a/src/util/cli.js +++ b/src/util/cli.js @@ -1,17 +1,17 @@ -// @format -// +/** @format */ + // Utility functions for CLI- and de8ugging-rel8ted stuff. // // A 8unch of these depend on process.stdout 8eing availa8le, so they won't // work within the 8rowser. -const { process } = globalThis; +const {process} = globalThis; export const ENABLE_COLOR = process && - ((process.env.CLICOLOR_FORCE && process.env.CLICOLOR_FORCE === "1") ?? + ((process.env.CLICOLOR_FORCE && process.env.CLICOLOR_FORCE === '1') ?? (process.env.CLICOLOR && - process.env.CLICOLOR === "1" && + process.env.CLICOLOR === '1' && process.stdout.hasColors && process.stdout.hasColors()) ?? (process.stdout.hasColors ? process.stdout.hasColors() : true)); @@ -20,17 +20,17 @@ const C = (n) => ENABLE_COLOR ? (text) => `\x1b[${n}m${text}\x1b[0m` : (text) => text; export const color = { - bright: C("1"), - dim: C("2"), - normal: C("22"), - black: C("30"), - red: C("31"), - green: C("32"), - yellow: C("33"), - blue: C("34"), - magenta: C("35"), - cyan: C("36"), - white: C("37"), + bright: C('1'), + dim: C('2'), + normal: C('22'), + black: C('30'), + red: C('31'), + green: C('32'), + yellow: C('33'), + blue: C('34'), + magenta: C('35'), + cyan: C('36'), + white: C('37'), }; const logColor = @@ -51,7 +51,7 @@ const logColor = } } wc(`\x1b[0m`); - w("\n"); + w('\n'); }; export const logInfo = logColor(2); @@ -105,9 +105,9 @@ export async function parseOptions(options, optionDescriptorMap) { const result = Object.create(null); for (let i = 0; i < options.length; i++) { const option = options[i]; - if (option.startsWith("--")) { + if (option.startsWith('--')) { // --x can be a flag or expect a value or series of values - let name = option.slice(2).split("=")[0]; // '--x'.split('=') = ['--x'] + let name = option.slice(2).split('=')[0]; // '--x'.split('=') = ['--x'] let descriptor = optionDescriptorMap[name]; if (!descriptor) { if (handleUnknown) { @@ -122,13 +122,13 @@ export async function parseOptions(options, optionDescriptorMap) { name = descriptor.alias; descriptor = optionDescriptorMap[name]; } - if (descriptor.type === "flag") { + if (descriptor.type === 'flag') { result[name] = true; - } else if (descriptor.type === "value") { - let value = option.slice(2).split("=")[1]; + } else if (descriptor.type === 'value') { + let value = option.slice(2).split('=')[1]; if (!value) { value = options[++i]; - if (!value || value.startsWith("-")) { + if (!value || value.startsWith('-')) { value = null; } } @@ -137,14 +137,14 @@ export async function parseOptions(options, optionDescriptorMap) { process.exit(1); } result[name] = value; - } else if (descriptor.type === "series") { - if (!options.slice(i).includes(";")) { + } else if (descriptor.type === 'series') { + if (!options.slice(i).includes(';')) { console.error( `Expected a series of values concluding with ; (\\;) for --${name}` ); process.exit(1); } - const endIndex = i + options.slice(i).indexOf(";"); + const endIndex = i + options.slice(i).indexOf(';'); result[name] = options.slice(i + 1, endIndex); i = endIndex; } @@ -155,7 +155,7 @@ export async function parseOptions(options, optionDescriptorMap) { process.exit(1); } } - } else if (option.startsWith("-")) { + } else if (option.startsWith('-')) { // mtui doesn't use any -x=y or -x y format optionuments // -x will always just be a flag let name = option.slice(1); @@ -173,7 +173,7 @@ export async function parseOptions(options, optionDescriptorMap) { name = descriptor.alias; descriptor = optionDescriptorMap[name]; } - if (descriptor.type === "flag") { + if (descriptor.type === 'flag') { result[name] = true; } else { console.error(`Use --${name} (value) to specify ${name}`); @@ -191,7 +191,7 @@ export const handleUnknown = Symbol(); export function decorateTime(arg1, arg2) { const [id, functionToBeWrapped] = - typeof arg1 === "string" || typeof arg1 === "symbol" + typeof arg1 === 'string' || typeof arg1 === 'symbol' ? [arg1, arg2] : [Symbol(arg1.name), arg1]; @@ -202,7 +202,7 @@ export function decorateTime(arg1, arg2) { displayTime() { const averageTime = meta.timeSpent / meta.timesCalled; console.log( - `\x1b[1m${typeof id === "symbol" ? id.description : id}(...):\x1b[0m ${ + `\x1b[1m${typeof id === 'symbol' ? id.description : id}(...):\x1b[0m ${ meta.timeSpent } ms / ${meta.timesCalled} calls \x1b[2m(avg: ${averageTime} ms)\x1b[0m` ); @@ -236,7 +236,7 @@ decorateTime.displayTime = function () { ]; if (keys.length) { - console.log(`\x1b[1mdecorateTime results: ` + "-".repeat(40) + "\x1b[0m"); + console.log(`\x1b[1mdecorateTime results: ` + '-'.repeat(40) + '\x1b[0m'); for (const key of keys) { map[key].displayTime(); } @@ -249,7 +249,7 @@ export function progressPromiseAll(msgOrMsgFn, array) { } const msgFn = - typeof msgOrMsgFn === "function" ? msgOrMsgFn : () => msgOrMsgFn; + typeof msgOrMsgFn === 'function' ? msgOrMsgFn : () => msgOrMsgFn; let done = 0, total = array.length; @@ -260,9 +260,9 @@ export function progressPromiseAll(msgOrMsgFn, array) { Promise.resolve(promise).then((val) => { done++; // const pc = `${done}/${total}`; - const pc = (Math.round((done / total) * 1000) / 10 + "%").padEnd( - "99.9%".length, - " " + const pc = (Math.round((done / total) * 1000) / 10 + '%').padEnd( + '99.9%'.length, + ' ' ); if (done === total) { const time = Date.now() - start; diff --git a/src/util/colors.js b/src/util/colors.js index 8d6b24d..5848a82 100644 --- a/src/util/colors.js +++ b/src/util/colors.js @@ -1,5 +1,5 @@ -// @format -// +/** @format */ + // Color and theming utility functions! Handy. // Graciously stolen from https://stackoverflow.com/a/54071699! ::::) diff --git a/src/util/find.js b/src/util/find.js index 6ee0583..460a4fa 100644 --- a/src/util/find.js +++ b/src/util/find.js @@ -1,14 +1,14 @@ -// @format +/** @format */ -import { color, logError, logWarn } from "./cli.js"; +import {color, logError, logWarn} from './cli.js'; -import { inspect } from "util"; +import {inspect} from 'util'; function warnOrThrow(mode, message) { switch (mode) { - case "error": + case 'error': throw new Error(message); - case "warn": + case 'warn': logWarn(message); default: return null; @@ -26,16 +26,16 @@ function findHelper(keys, findFns = {}) { const byName = findFns.byName || matchName; const keyRefRegex = new RegExp( - String.raw`^(?:(${keys.join("|")}):(?=\S))?(.*)$` + String.raw`^(?:(${keys.join('|')}):(?=\S))?(.*)$` ); // The mode argument here may be 'warn', 'error', or 'quiet'. 'error' throws // errors for null matches (with details about the error), while 'warn' and // 'quiet' both return null, with 'warn' logging details directly to the // console. - return (fullRef, data, { mode = "warn" } = {}) => { + return (fullRef, data, {mode = 'warn'} = {}) => { if (!fullRef) return null; - if (typeof fullRef !== "string") { + if (typeof fullRef !== 'string') { throw new Error( `Got a reference that is ${typeof fullRef}, not string: ${fullRef}` ); @@ -81,19 +81,19 @@ function findHelper(keys, findFns = {}) { } function matchDirectory(ref, data, mode) { - return data.find(({ directory }) => directory === ref); + return data.find(({directory}) => directory === ref); } function matchName(ref, data, mode) { const matches = data.filter( - ({ name }) => name.toLowerCase() === ref.toLowerCase() + ({name}) => name.toLowerCase() === ref.toLowerCase() ); if (matches.length > 1) { return warnOrThrow( mode, `Multiple matches for reference "${ref}". Please resolve:\n` + - matches.map((match) => `- ${inspect(match)}\n`).join("") + + matches.map((match) => `- ${inspect(match)}\n`).join('') + `Returning null for this reference.` ); } @@ -115,19 +115,19 @@ function matchName(ref, data, mode) { } function matchTagName(ref, data, quiet) { - return matchName(ref.startsWith("cw: ") ? ref.slice(4) : ref, data, quiet); + return matchName(ref.startsWith('cw: ') ? ref.slice(4) : ref, data, quiet); } const find = { - album: findHelper(["album", "album-commentary"]), - artist: findHelper(["artist", "artist-gallery"]), - artTag: findHelper(["tag"], { byName: matchTagName }), - flash: findHelper(["flash"]), - group: findHelper(["group", "group-gallery"]), - listing: findHelper(["listing"]), - newsEntry: findHelper(["news-entry"]), - staticPage: findHelper(["static"]), - track: findHelper(["track"]), + album: findHelper(['album', 'album-commentary']), + artist: findHelper(['artist', 'artist-gallery']), + artTag: findHelper(['tag'], {byName: matchTagName}), + flash: findHelper(['flash']), + group: findHelper(['group', 'group-gallery']), + listing: findHelper(['listing']), + newsEntry: findHelper(['news-entry']), + staticPage: findHelper(['static']), + track: findHelper(['track']), }; export default find; @@ -140,15 +140,15 @@ export default find; export function bindFind(wikiData, opts1) { return Object.fromEntries( Object.entries({ - album: "albumData", - artist: "artistData", - artTag: "artTagData", - flash: "flashData", - group: "groupData", - listing: "listingSpec", - newsEntry: "newsData", - staticPage: "staticPageData", - track: "trackData", + album: 'albumData', + artist: 'artistData', + artTag: 'artTagData', + flash: 'flashData', + group: 'groupData', + listing: 'listingSpec', + newsEntry: 'newsData', + staticPage: 'staticPageData', + track: 'trackData', }).map(([key, value]) => { const findFn = find[key]; const thingData = wikiData[value]; @@ -157,7 +157,7 @@ export function bindFind(wikiData, opts1) { opts1 ? (ref, opts2) => opts2 - ? findFn(ref, thingData, { ...opts1, ...opts2 }) + ? findFn(ref, thingData, {...opts1, ...opts2}) : findFn(ref, thingData, opts1) : (ref, opts2) => opts2 ? findFn(ref, thingData, opts2) : findFn(ref, thingData), diff --git a/src/util/html.js b/src/util/html.js index 913dc7b..f5b7bdc 100644 --- a/src/util/html.js +++ b/src/util/html.js @@ -1,23 +1,23 @@ -// @format -// +/** @format */ + // Some really simple functions for formatting HTML content. // COMPREHENSIVE! // https://html.spec.whatwg.org/multipage/syntax.html#void-elements export const selfClosingTags = [ - "area", - "base", - "br", - "col", - "embed", - "hr", - "img", - "input", - "link", - "meta", - "source", - "track", - "wbr", + 'area', + 'base', + 'br', + 'col', + 'embed', + 'hr', + 'img', + 'input', + 'link', + 'meta', + 'source', + 'track', + 'wbr', ]; // Pass to tag() as an attri8utes key to make tag() return a 8lank string @@ -32,7 +32,7 @@ export function tag(tagName, ...args) { let content; let attrs; - if (typeof args[0] === "object" && !Array.isArray(args[0])) { + if (typeof args[0] === 'object' && !Array.isArray(args[0])) { attrs = args[0]; content = args[1]; } else { @@ -44,7 +44,7 @@ export function tag(tagName, ...args) { } if (attrs?.[onlyIfContent] && !content) { - return ""; + return ''; } if (attrs) { @@ -59,17 +59,17 @@ export function tag(tagName, ...args) { } if (Array.isArray(content)) { - content = content.filter(Boolean).join("\n"); + content = content.filter(Boolean).join('\n'); } if (content) { - if (content.includes("\n")) { + if (content.includes('\n')) { return ( `<${openTag}>\n` + content - .split("\n") - .map((line) => " " + line + "\n") - .join("") + + .split('\n') + .map((line) => ' ' + line + '\n') + .join('') + `` ); } else { @@ -85,18 +85,18 @@ export function tag(tagName, ...args) { } export function escapeAttributeValue(value) { - return value.replaceAll('"', """).replaceAll("'", "'"); + return value.replaceAll('"', '"').replaceAll("'", '''); } export function attributes(attribs) { return Object.entries(attribs) .map(([key, val]) => { - if (typeof val === "undefined" || val === null) return [key, val, false]; - else if (typeof val === "string") return [key, val, true]; - else if (typeof val === "boolean") return [key, val, val]; - else if (typeof val === "number") return [key, val.toString(), true]; + if (typeof val === 'undefined' || val === null) return [key, val, false]; + else if (typeof val === 'string') return [key, val, true]; + else if (typeof val === 'boolean') return [key, val, val]; + else if (typeof val === 'number') return [key, val.toString(), true]; else if (Array.isArray(val)) - return [key, val.filter(Boolean).join(" "), val.length > 0]; + return [key, val.filter(Boolean).join(' '), val.length > 0]; else throw new Error( `Attribute value for ${key} should be primitive or array, got ${typeof val}` @@ -104,9 +104,9 @@ export function attributes(attribs) { }) .filter(([key, val, keep]) => keep) .map(([key, val]) => - typeof val === "boolean" + typeof val === 'boolean' ? `${key}` : `${key}="${escapeAttributeValue(val)}"` ) - .join(" "); + .join(' '); } diff --git a/src/util/io.js b/src/util/io.js index 6ea1e22..5c1ab24 100644 --- a/src/util/io.js +++ b/src/util/io.js @@ -1,14 +1,14 @@ -// @format -// +/** @format */ + // Utility functions for interacting with files and other external data // interfacey constructs. -import { readdir } from "fs/promises"; -import * as path from "path"; +import {readdir} from 'fs/promises'; +import * as path from 'path'; export async function findFiles( dataPath, - { filter = (f) => true, joinParentDirectory = true } = {} + {filter = (f) => true, joinParentDirectory = true} = {} ) { return (await readdir(dataPath)) .filter((file) => filter(file)) diff --git a/src/util/link.js b/src/util/link.js index 4095b17..ee3579d 100644 --- a/src/util/link.js +++ b/src/util/link.js @@ -1,5 +1,5 @@ -// @format -// +/** @format */ + // This file is essentially one level of a8straction a8ove urls.js (and the // urlSpec it gets its paths from). It's a 8unch of utility functions which // take certain types of wiki data o8jects (colloquially known as "things") @@ -11,74 +11,74 @@ // options availa8le in all the functions, making a common interface for // gener8ting just a8out any link on the site. -import * as html from "./html.js"; -import { getColors } from "./colors.js"; +import * as html from './html.js'; +import {getColors} from './colors.js'; export function getLinkThemeString(color) { - if (!color) return ""; + if (!color) return ''; - const { primary, dim } = getColors(color); + const {primary, dim} = getColors(color); return `--primary-color: ${primary}; --dim-color: ${dim}`; } const appendIndexHTMLRegex = /^(?!https?:\/\/).+\/$/; const linkHelper = - (hrefFn, { color = true, attr = null } = {}) => + (hrefFn, {color = true, attr = null} = {}) => ( thing, { to, - text = "", + text = '', attributes = null, - class: className = "", + class: className = '', color: color2 = true, - hash = "", + hash = '', } ) => { - let href = hrefFn(thing, { to }); + let href = hrefFn(thing, {to}); if (link.globalOptions.appendIndexHTML) { if (appendIndexHTMLRegex.test(href)) { - href += "index.html"; + href += 'index.html'; } } if (hash) { - href += (hash.startsWith("#") ? "" : "#") + hash; + href += (hash.startsWith('#') ? '' : '#') + hash; } return html.tag( - "a", + 'a', { ...(attr ? attr(thing) : {}), ...(attributes ? attributes : {}), href, style: - typeof color2 === "string" + typeof color2 === 'string' ? getLinkThemeString(color2) : color2 && color ? getLinkThemeString(thing.color) - : "", + : '', class: className, }, text || thing.name ); }; -const linkDirectory = (key, { expose = null, attr = null, ...conf } = {}) => - linkHelper((thing, { to }) => to("localized." + key, thing.directory), { +const linkDirectory = (key, {expose = null, attr = null, ...conf} = {}) => + linkHelper((thing, {to}) => to('localized.' + key, thing.directory), { attr: (thing) => ({ ...(attr ? attr(thing) : {}), - ...(expose ? { [expose]: thing.directory } : {}), + ...(expose ? {[expose]: thing.directory} : {}), }), ...conf, }); const linkPathname = (key, conf) => - linkHelper(({ directory: pathname }, { to }) => to(key, pathname), conf); + linkHelper(({directory: pathname}, {to}) => to(key, pathname), conf); const linkIndex = (key, conf) => - linkHelper((_, { to }) => to("localized." + key), conf); + linkHelper((_, {to}) => to('localized.' + key), conf); const link = { globalOptions: { @@ -90,50 +90,50 @@ const link = { appendIndexHTML: false, }, - album: linkDirectory("album"), - albumCommentary: linkDirectory("albumCommentary"), - artist: linkDirectory("artist", { color: false }), - artistGallery: linkDirectory("artistGallery", { color: false }), - commentaryIndex: linkIndex("commentaryIndex", { color: false }), - flashIndex: linkIndex("flashIndex", { color: false }), - flash: linkDirectory("flash"), - groupInfo: linkDirectory("groupInfo"), - groupGallery: linkDirectory("groupGallery"), - home: linkIndex("home", { color: false }), - listingIndex: linkIndex("listingIndex"), - listing: linkDirectory("listing"), - newsIndex: linkIndex("newsIndex", { color: false }), - newsEntry: linkDirectory("newsEntry", { color: false }), - staticPage: linkDirectory("staticPage", { color: false }), - tag: linkDirectory("tag"), - track: linkDirectory("track", { expose: "data-track" }), + album: linkDirectory('album'), + albumCommentary: linkDirectory('albumCommentary'), + artist: linkDirectory('artist', {color: false}), + artistGallery: linkDirectory('artistGallery', {color: false}), + commentaryIndex: linkIndex('commentaryIndex', {color: false}), + flashIndex: linkIndex('flashIndex', {color: false}), + flash: linkDirectory('flash'), + groupInfo: linkDirectory('groupInfo'), + groupGallery: linkDirectory('groupGallery'), + home: linkIndex('home', {color: false}), + listingIndex: linkIndex('listingIndex'), + listing: linkDirectory('listing'), + newsIndex: linkIndex('newsIndex', {color: false}), + newsEntry: linkDirectory('newsEntry', {color: false}), + staticPage: linkDirectory('staticPage', {color: false}), + tag: linkDirectory('tag'), + track: linkDirectory('track', {expose: 'data-track'}), // TODO: This is a bit hacky. Files are just strings (not objects), so we // have to manually provide the album alongside the file. They also don't // follow the usual {name: whatever} type shape, so we have to provide that // ourselves. _albumAdditionalFileHelper: linkHelper( - (fakeFileObject, { to }) => + (fakeFileObject, {to}) => to( - "media.albumAdditionalFile", + 'media.albumAdditionalFile', fakeFileObject.album.directory, fakeFileObject.name ), - { color: false } + {color: false} ), - albumAdditionalFile: ({ file, album }, { to }) => + albumAdditionalFile: ({file, album}, {to}) => link._albumAdditionalFileHelper( { name: file, album, }, - { to } + {to} ), - media: linkPathname("media.path", { color: false }), - root: linkPathname("shared.path", { color: false }), - data: linkPathname("data.path", { color: false }), - site: linkPathname("localized.path", { color: false }), + media: linkPathname('media.path', {color: false}), + root: linkPathname('shared.path', {color: false}), + data: linkPathname('data.path', {color: false}), + site: linkPathname('localized.path', {color: false}), }; export default link; diff --git a/src/util/magic-constants.js b/src/util/magic-constants.js index a7b2933..dbdbcfd 100644 --- a/src/util/magic-constants.js +++ b/src/util/magic-constants.js @@ -1,5 +1,5 @@ -// @format -// +/** @format */ + // Magic constants only! These are hard-coded, and any use of them should be // considered a flaw in the codebase - areas where we use hard-coded behavior // to support one use of the wiki software (i.e. HSMusic, usually), rather than @@ -8,5 +8,5 @@ // All such uses should eventually be replaced with better code in due time // (TM). -export const OFFICIAL_GROUP_DIRECTORY = "official"; -export const FANDOM_GROUP_DIRECTORY = "fandom"; +export const OFFICIAL_GROUP_DIRECTORY = 'official'; +export const FANDOM_GROUP_DIRECTORY = 'fandom'; diff --git a/src/util/node-utils.js b/src/util/node-utils.js index f638e4a..df44665 100644 --- a/src/util/node-utils.js +++ b/src/util/node-utils.js @@ -1,10 +1,10 @@ -// @format -// +/** @format */ + // Utility functions which are only relevant to particular Node.js constructs. -import { fileURLToPath } from "url"; +import {fileURLToPath} from 'url'; -import _commandExists from "command-exists"; +import _commandExists from 'command-exists'; // This package throws an error instead of returning false when the command // doesn't exist, for some reason. Yay for making logic more difficult! @@ -32,7 +32,7 @@ export function promisifyProcess(proc, showLogging = true) { proc.stderr.pipe(process.stderr); } - proc.on("exit", (code) => { + proc.on('exit', (code) => { if (code === 0) { resolve(); } else { diff --git a/src/util/replacer.js b/src/util/replacer.js index 10603b6..6f4d0e9 100644 --- a/src/util/replacer.js +++ b/src/util/replacer.js @@ -1,14 +1,14 @@ -// @format +/** @format */ -import { logError, logWarn } from "./cli.js"; -import { escapeRegex } from "./sugar.js"; +import {logError, logWarn} from './cli.js'; +import {escapeRegex} from './sugar.js'; -export function validateReplacerSpec(replacerSpec, { find, link }) { +export function validateReplacerSpec(replacerSpec, {find, link}) { let success = true; for (const [ key, - { link: linkKey, find: findKey, value, html }, + {link: linkKey, find: findKey, value, html}, ] of Object.entries(replacerSpec)) { if (!html && !link[linkKey]) { logError`The replacer spec ${key} has invalid link key ${linkKey}! Specify it in link specs or fix typo.`; @@ -24,15 +24,15 @@ export function validateReplacerSpec(replacerSpec, { find, link }) { } // Syntax literals. -const tagBeginning = "[["; -const tagEnding = "]]"; -const tagReplacerValue = ":"; -const tagHash = "#"; -const tagArgument = "*"; -const tagArgumentValue = "="; -const tagLabel = "|"; +const tagBeginning = '[['; +const tagEnding = ']]'; +const tagReplacerValue = ':'; +const tagHash = '#'; +const tagArgument = '*'; +const tagArgumentValue = '='; +const tagLabel = '|'; -const noPrecedingWhitespace = "(? ({ i, type: "error", data: { message } }); +const makeError = (i, message) => ({i, type: 'error', data: {message}}); const endOfInput = (i, comment) => makeError(i, `Unexpected end of input (${comment}).`); @@ -67,7 +67,7 @@ function parseOneTextNode(input, i, stopAt) { function parseNodes(input, i, stopAt, textOnly) { let nodes = []; let escapeNext = false; - let string = ""; + let string = ''; let iString = 0; stopped = false; @@ -82,8 +82,8 @@ function parseNodes(input, i, stopAt, textOnly) { } if (string.length) { - nodes.push({ i: iString, iEnd: i, type: "text", data: string }); - string = ""; + nodes.push({i: iString, iEnd: i, type: 'text', data: string}); + string = ''; } }; @@ -97,7 +97,7 @@ function parseNodes(input, i, stopAt, textOnly) { // should 8e counted only as part of the current string/text. // // Inspired 8y this: https://stackoverflow.com/a/41470813 - const regexpSource = `(?= 0) { lineStart += 1; } else { lineStart = 0; } - let lineEnd = input.slice(i).indexOf("\n"); + let lineEnd = input.slice(i).indexOf('\n'); if (lineEnd >= 0) { lineEnd += i; } else { @@ -334,18 +334,18 @@ export function parseInput(input) { throw new SyntaxError(fixWS` Parse error (at pos ${i}): ${message} ${line} - ${"-".repeat(cursor) + "^"} + ${'-'.repeat(cursor) + '^'} `); } } function evaluateTag(node, opts) { - const { find, input, language, link, replacerSpec, to, wikiData } = opts; + const {find, input, language, link, replacerSpec, to, wikiData} = opts; const source = input.slice(node.i, node.iEnd); const replacerKeyImplied = !node.data.replacerKey; - const replacerKey = replacerKeyImplied ? "track" : node.data.replacerKey.data; + const replacerKey = replacerKeyImplied ? 'track' : node.data.replacerKey.data; if (!replacerSpec[replacerKey]) { logWarn`The link ${source} has an invalid replacer key!`; @@ -395,7 +395,7 @@ function evaluateTag(node, opts) { const args = node.data.args && Object.fromEntries( - node.data.args.map(({ key, value }) => [ + node.data.args.map(({key, value}) => [ transformNode(key, opts), transformNodes(value, opts), ]) @@ -404,7 +404,7 @@ function evaluateTag(node, opts) { const fn = htmlFn ? htmlFn : link[linkKey]; try { - return fn(value, { text: label, hash, args, language, to }); + return fn(value, {text: label, hash, args, language, to}); } catch (error) { logError`The link ${source} failed to be processed: ${error}`; return source; @@ -413,17 +413,17 @@ function evaluateTag(node, opts) { function transformNode(node, opts) { if (!node) { - throw new Error("Expected a node!"); + throw new Error('Expected a node!'); } if (Array.isArray(node)) { - throw new Error("Got an array - use transformNodes here!"); + throw new Error('Got an array - use transformNodes here!'); } switch (node.type) { - case "text": + case 'text': return node.data; - case "tag": + case 'tag': return evaluateTag(node, opts); default: throw new Error(`Unknown node type ${node.type}`); @@ -435,19 +435,19 @@ function transformNodes(nodes, opts) { throw new Error(`Expected an array of nodes! Got: ${nodes}`); } - return nodes.map((node) => transformNode(node, opts)).join(""); + return nodes.map((node) => transformNode(node, opts)).join(''); } export function transformInline( input, - { replacerSpec, find, link, language, to, wikiData } + {replacerSpec, find, link, language, to, wikiData} ) { - if (!replacerSpec) throw new Error("Expected replacerSpec"); - if (!find) throw new Error("Expected find"); - if (!link) throw new Error("Expected link"); - if (!language) throw new Error("Expected language"); - if (!to) throw new Error("Expected to"); - if (!wikiData) throw new Error("Expected wikiData"); + if (!replacerSpec) throw new Error('Expected replacerSpec'); + if (!find) throw new Error('Expected find'); + if (!link) throw new Error('Expected link'); + if (!language) throw new Error('Expected language'); + if (!to) throw new Error('Expected to'); + if (!wikiData) throw new Error('Expected wikiData'); const nodes = parseInput(input); return transformNodes(nodes, { diff --git a/src/util/serialize.js b/src/util/serialize.js index ab86483..9aa8b0c 100644 --- a/src/util/serialize.js +++ b/src/util/serialize.js @@ -1,4 +1,4 @@ -// @format +/** @format */ export function serializeLink(thing) { const ret = {}; @@ -9,7 +9,7 @@ export function serializeLink(thing) { } export function serializeContribs(contribs) { - return contribs.map(({ who, what }) => { + return contribs.map(({who, what}) => { const ret = {}; ret.artist = serializeLink(who); if (what) ret.contribution = what; @@ -17,7 +17,7 @@ export function serializeContribs(contribs) { }); } -export function serializeImagePaths(original, { thumb }) { +export function serializeImagePaths(original, {thumb}) { return { original, medium: thumb.medium(original), @@ -28,13 +28,13 @@ export function serializeImagePaths(original, { thumb }) { export function serializeCover( thing, pathFunction, - { serializeImagePaths, urls } + {serializeImagePaths, urls} ) { const coverPath = pathFunction(thing, { - to: urls.from("media.root").to, + to: urls.from('media.root').to, }); - const { artTags } = thing; + const {artTags} = thing; const cwTags = artTags.filter((tag) => tag.isContentWarning); const linkTags = artTags.filter((tag) => !tag.isContentWarning); @@ -46,15 +46,15 @@ export function serializeCover( }; } -export function serializeGroupsForAlbum(album, { serializeLink }) { +export function serializeGroupsForAlbum(album, {serializeLink}) { return album.groups .map((group) => { const index = group.albums.indexOf(album); const next = group.albums[index + 1] || null; const previous = group.albums[index - 1] || null; - return { group, index, next, previous }; + return {group, index, next, previous}; }) - .map(({ group, index, next, previous }) => ({ + .map(({group, index, next, previous}) => ({ link: serializeLink(group), descriptionShort: group.descriptionShort, albumIndex: index, @@ -64,7 +64,7 @@ export function serializeGroupsForAlbum(album, { serializeLink }) { })); } -export function serializeGroupsForTrack(track, { serializeLink }) { +export function serializeGroupsForTrack(track, {serializeLink}) { return track.album.groups.map((group) => ({ link: serializeLink(group), urls: group.urls, diff --git a/src/util/sugar.js b/src/util/sugar.js index a4504da..0a5de48 100644 --- a/src/util/sugar.js +++ b/src/util/sugar.js @@ -1,5 +1,5 @@ -// @format -// +/** @format */ + // Syntactic sugar! (Mostly.) // Generic functions - these are useful just a8out everywhere. // @@ -8,7 +8,7 @@ // It will likely only do exactly what I want it to, and only in the cases I // decided were relevant enough to 8other handling. -import { color } from "./cli.js"; +import {color} from './cli.js'; // Apparently JavaScript doesn't come with a function to split an array into // chunks! Weird. Anyway, this is an awesome place to use a generator, even @@ -35,13 +35,13 @@ export const mapInPlace = (array, fn) => export const filterEmptyLines = (string) => string - .split("\n") + .split('\n') .filter((line) => line.trim()) - .join("\n"); + .join('\n'); export const unique = (arr) => Array.from(new Set(arr)); -export const compareArrays = (arr1, arr2, { checkOrder = true } = {}) => +export const compareArrays = (arr1, arr2, {checkOrder = true} = {}) => arr1.length === arr2.length && (checkOrder ? arr1.every((x, i) => arr2[i] === x) @@ -90,7 +90,7 @@ export function delay(ms) { // There's a proposal for a native JS function like this, 8ut it's not even // past stage 1 yet: https://github.com/tc39/proposal-regex-escaping export function escapeRegex(string) { - return string.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&"); + return string.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); } export function bindOpts(fn, bind) { @@ -98,10 +98,10 @@ export function bindOpts(fn, bind) { const bound = function (...args) { const opts = args[bindIndex] ?? {}; - return fn(...args.slice(0, bindIndex), { ...bind, ...opts }); + return fn(...args.slice(0, bindIndex), {...bind, ...opts}); }; - Object.defineProperty(bound, "name", { + Object.defineProperty(bound, 'name', { value: fn.name ? `(options-bound) ${fn.name}` : `(options-bound)`, }); @@ -132,7 +132,7 @@ export function openAggregate({ // Optional human-readable message to describe the aggregate error, if // constructed. - message = "", + message = '', // Value to return when a provided function throws an error. If this is a // function, it will be called with the arguments given to the function. @@ -151,7 +151,7 @@ export function openAggregate({ return fn(...args); } catch (error) { errors.push(error); - return typeof returnOnFail === "function" + return typeof returnOnFail === 'function' ? returnOnFail(...args) : returnOnFail; } @@ -164,7 +164,7 @@ export function openAggregate({ (value) => value, (error) => { errors.push(error); - return typeof returnOnFail === "function" + return typeof returnOnFail === 'function' ? returnOnFail(...args) : returnOnFail; } @@ -189,21 +189,21 @@ export function openAggregate({ aggregate.map = (...args) => { const parent = aggregate; - const { result, aggregate: child } = mapAggregate(...args); + const {result, aggregate: child} = mapAggregate(...args); parent.call(child.close); return result; }; aggregate.mapAsync = async (...args) => { const parent = aggregate; - const { result, aggregate: child } = await mapAggregateAsync(...args); + const {result, aggregate: child} = await mapAggregateAsync(...args); parent.call(child.close); return result; }; aggregate.filter = (...args) => { const parent = aggregate; - const { result, aggregate: child } = filterAggregate(...args); + const {result, aggregate: child} = filterAggregate(...args); parent.call(child.close); return result; }; @@ -219,11 +219,11 @@ export function openAggregate({ return aggregate; } -openAggregate.errorClassSymbol = Symbol("error class"); +openAggregate.errorClassSymbol = Symbol('error class'); // Utility function for providing {errorClass} parameter to aggregate functions. export function aggregateThrows(errorClass) { - return { [openAggregate.errorClassSymbol]: errorClass }; + return {[openAggregate.errorClassSymbol]: errorClass}; } // Performs an ordinary array map with the given function, collating into a @@ -236,15 +236,15 @@ export function aggregateThrows(errorClass) { // use aggregate.close() to throw the error. (This aggregate may be passed to a // parent aggregate: `parent.call(aggregate.close)`!) export function mapAggregate(array, fn, aggregateOpts) { - return _mapAggregate("sync", null, array, fn, aggregateOpts); + return _mapAggregate('sync', null, array, fn, aggregateOpts); } export function mapAggregateAsync( array, fn, - { promiseAll = Promise.all.bind(Promise), ...aggregateOpts } = {} + {promiseAll = Promise.all.bind(Promise), ...aggregateOpts} = {} ) { - return _mapAggregate("async", promiseAll, array, fn, aggregateOpts); + return _mapAggregate('async', promiseAll, array, fn, aggregateOpts); } // Helper function for mapAggregate which holds code common between sync and @@ -257,15 +257,15 @@ export function _mapAggregate(mode, promiseAll, array, fn, aggregateOpts) { ...aggregateOpts, }); - if (mode === "sync") { + if (mode === 'sync') { const result = array .map(aggregate.wrap(fn)) .filter((value) => value !== failureSymbol); - return { result, aggregate }; + return {result, aggregate}; } else { return promiseAll(array.map(aggregate.wrapAsync(fn))).then((values) => { const result = values.filter((value) => value !== failureSymbol); - return { result, aggregate }; + return {result, aggregate}; }); } } @@ -278,15 +278,15 @@ export function _mapAggregate(mode, promiseAll, array, fn, aggregateOpts) { // // As with mapAggregate, the returned aggregate property is not yet closed. export function filterAggregate(array, fn, aggregateOpts) { - return _filterAggregate("sync", null, array, fn, aggregateOpts); + return _filterAggregate('sync', null, array, fn, aggregateOpts); } export async function filterAggregateAsync( array, fn, - { promiseAll = Promise.all.bind(Promise), ...aggregateOpts } = {} + {promiseAll = Promise.all.bind(Promise), ...aggregateOpts} = {} ) { - return _filterAggregate("async", promiseAll, array, fn, aggregateOpts); + return _filterAggregate('async', promiseAll, array, fn, aggregateOpts); } // Helper function for filterAggregate which holds code common between sync and @@ -326,30 +326,30 @@ function _filterAggregate(mode, promiseAll, array, fn, aggregateOpts) { }; } - if (mode === "sync") { + if (mode === 'sync') { const result = array .map( aggregate.wrap((input, index, array) => { const output = fn(input, index, array); - return { input, output }; + return {input, output}; }) ) .filter(filterFunction) .map(mapFunction); - return { result, aggregate }; + return {result, aggregate}; } else { return promiseAll( array.map( aggregate.wrapAsync(async (input, index, array) => { const output = await fn(input, index, array); - return { input, output }; + return {input, output}; }) ) ).then((values) => { const result = values.filter(filterFunction).map(mapFunction); - return { result, aggregate }; + return {result, aggregate}; }); } } @@ -358,22 +358,22 @@ function _filterAggregate(mode, promiseAll, array, fn, aggregateOpts) { // function with it, then closing the function and returning the result (if // there's no throw). export function withAggregate(aggregateOpts, fn) { - return _withAggregate("sync", aggregateOpts, fn); + return _withAggregate('sync', aggregateOpts, fn); } export function withAggregateAsync(aggregateOpts, fn) { - return _withAggregate("async", aggregateOpts, fn); + return _withAggregate('async', aggregateOpts, fn); } export function _withAggregate(mode, aggregateOpts, fn) { - if (typeof aggregateOpts === "function") { + if (typeof aggregateOpts === 'function') { fn = aggregateOpts; aggregateOpts = {}; } const aggregate = openAggregate(aggregateOpts); - if (mode === "sync") { + if (mode === 'sync') { const result = fn(aggregate); aggregate.close(); return result; @@ -387,56 +387,56 @@ export function _withAggregate(mode, aggregateOpts, fn) { export function showAggregate( topError, - { pathToFile = (p) => p, showTraces = true } = {} + {pathToFile = (p) => p, showTraces = true} = {} ) { - const recursive = (error, { level }) => { + const recursive = (error, {level}) => { let header = showTraces - ? `[${error.constructor.name || "unnamed"}] ${ - error.message || "(no message)" + ? `[${error.constructor.name || 'unnamed'}] ${ + error.message || '(no message)' }` : error instanceof AggregateError - ? `[${error.message || "(no message)"}]` - : error.message || "(no message)"; + ? `[${error.message || '(no message)'}]` + : error.message || '(no message)'; if (showTraces) { - const stackLines = error.stack?.split("\n"); + const stackLines = error.stack?.split('\n'); const stackLine = stackLines?.find( (line) => - line.trim().startsWith("at") && - !line.includes("sugar") && - !line.includes("node:") && - !line.includes("") + line.trim().startsWith('at') && + !line.includes('sugar') && + !line.includes('node:') && + !line.includes('') ); const tracePart = stackLine - ? "- " + + ? '- ' + stackLine .trim() .replace(/file:\/\/(.*\.js)/, (match, pathname) => pathToFile(pathname) ) - : "(no stack trace)"; + : '(no stack trace)'; header += ` ${color.dim(tracePart)}`; } - const bar = level % 2 === 0 ? "\u2502" : color.dim("\u254e"); - const head = level % 2 === 0 ? "\u257f" : color.dim("\u257f"); + const bar = level % 2 === 0 ? '\u2502' : color.dim('\u254e'); + const head = level % 2 === 0 ? '\u257f' : color.dim('\u257f'); if (error instanceof AggregateError) { return ( header + - "\n" + + '\n' + error.errors - .map((error) => recursive(error, { level: level + 1 })) - .flatMap((str) => str.split("\n")) + .map((error) => recursive(error, {level: level + 1})) + .flatMap((str) => str.split('\n')) .map((line, i, lines) => i === 0 ? ` ${head} ${line}` : ` ${bar} ${line}` ) - .join("\n") + .join('\n') ); } else { return header; } }; - console.error(recursive(topError, { level: 0 })); + console.error(recursive(topError, {level: 0})); } export function decorateErrorWithIndex(fn) { diff --git a/src/util/urls.js b/src/util/urls.js index 4e39808..ce747df 100644 --- a/src/util/urls.js +++ b/src/util/urls.js @@ -1,5 +1,5 @@ -// @format -// +/** @format */ + // Code that deals with URLs (really the pathnames that get referenced all // throughout the gener8ted HTML). Most nota8ly here is generateURLs, which // is in charge of pre-gener8ting a complete network of template strings @@ -10,12 +10,12 @@ // actual path strings. More a8stract operations using wiki data o8jects is // the domain of link.js. -import * as path from "path"; -import { withEntries } from "./sugar.js"; +import * as path from 'path'; +import {withEntries} from './sugar.js'; export function generateURLs(urlSpec) { const getValueForFullKey = (obj, fullKey, prop = null) => { - const [groupKey, subKey] = fullKey.split("."); + const [groupKey, subKey] = fullKey.split('.'); if (!groupKey || !subKey) { throw new Error(`Expected group key and subkey (got ${fullKey})`); } @@ -41,27 +41,27 @@ export function generateURLs(urlSpec) { // This should be called on values which are going to be passed to // path.relative, because relative will resolve a leading slash as the root // directory of the working device, which we aren't looking for here. - const trimLeadingSlash = (P) => (P.startsWith("/") ? P.slice(1) : P); + const trimLeadingSlash = (P) => (P.startsWith('/') ? P.slice(1) : P); const generateTo = (fromPath, fromGroup) => { const A = trimLeadingSlash(fromPath); - const rebasePrefix = "../".repeat( - (fromGroup.prefix || "").split("/").filter(Boolean).length + const rebasePrefix = '../'.repeat( + (fromGroup.prefix || '').split('/').filter(Boolean).length ); const pathHelper = (toPath, toGroup) => { let B = trimLeadingSlash(toPath); let argIndex = 0; - B = B.replaceAll("<>", () => `<${argIndex++}>`); + B = B.replaceAll('<>', () => `<${argIndex++}>`); if (toGroup.prefix !== fromGroup.prefix) { // TODO: Handle differing domains in prefixes. - B = rebasePrefix + (toGroup.prefix || "") + B; + B = rebasePrefix + (toGroup.prefix || '') + B; } - const suffix = toPath.endsWith("/") ? "/" : ""; + const suffix = toPath.endsWith('/') ? '/' : ''; return { posix: path.posix.relative(A, B) + suffix, @@ -86,7 +86,7 @@ export function generateURLs(urlSpec) { (delimiterMode) => (key, ...args) => { const { - value: { [delimiterMode]: template }, + value: {[delimiterMode]: template}, } = getValueForFullKey(relative, key); let missing = 0; @@ -110,8 +110,8 @@ export function generateURLs(urlSpec) { }; return { - to: toHelper("posix"), - toDevice: toHelper("device"), + to: toHelper('posix'), + toDevice: toHelper('device'), }; }; @@ -127,16 +127,16 @@ export function generateURLs(urlSpec) { const from = (key) => getValueForFullKey(map, key).value; - return { from, map }; + return {from, map}; }; return generateFrom(); } const thumbnailHelper = (name) => (file) => - file.replace(/\.(jpg|png)$/, name + ".jpg"); + file.replace(/\.(jpg|png)$/, name + '.jpg'); export const thumb = { - medium: thumbnailHelper(".medium"), - small: thumbnailHelper(".small"), + medium: thumbnailHelper('.medium'), + small: thumbnailHelper('.small'), }; diff --git a/src/util/wiki-data.js b/src/util/wiki-data.js index 7e16580..65eb7d7 100644 --- a/src/util/wiki-data.js +++ b/src/util/wiki-data.js @@ -1,17 +1,17 @@ -// @format -// +/** @format */ + // Utility functions for interacting with wiki data. // Generic value operations export function getKebabCase(name) { return name - .split(" ") - .join("-") - .replace(/&/g, "and") - .replace(/[^a-zA-Z0-9\-]/g, "") - .replace(/-{2,}/g, "-") - .replace(/^-+|-+$/g, "") + .split(' ') + .join('-') + .replace(/&/g, 'and') + .replace(/[^a-zA-Z0-9\-]/g, '') + .replace(/-{2,}/g, '-') + .replace(/^-+|-+$/g, '') .toLowerCase(); } @@ -81,8 +81,8 @@ export function compareCaseLessSensitive(a, b) { const bl = b.toLowerCase(); return al === bl - ? a.localeCompare(b, undefined, { numeric: true }) - : al.localeCompare(bl, undefined, { numeric: true }); + ? a.localeCompare(b, undefined, {numeric: true}) + : al.localeCompare(bl, undefined, {numeric: true}); } // Subtract common prefixes and other characters which some people don't like @@ -92,22 +92,22 @@ export function normalizeName(s) { // Turn (some) ligatures into expanded variant for cleaner sorting, e.g. // "ff" into "ff", in decompose mode, so that "ü" is represented as two // bytes ("u" + \u0308 combining diaeresis). - s = s.normalize("NFKD"); + s = s.normalize('NFKD'); // Replace one or more whitespace of any kind in a row, as well as certain // punctuation, with a single typical space, then trim the ends. s = s .replace( /[\p{Separator}\p{Dash_Punctuation}\p{Connector_Punctuation}]+/gu, - " " + ' ' ) .trim(); // Discard anything that isn't a letter, number, or space. - s = s.replace(/[^\p{Letter}\p{Number} ]/gu, ""); + s = s.replace(/[^\p{Letter}\p{Number} ]/gu, ''); // Remove common English (only, for now) prefixes. - s = s.replace(/^(?:an?|the) /i, ""); + s = s.replace(/^(?:an?|the) /i, ''); return s; } @@ -142,7 +142,7 @@ export function normalizeName(s) { // except when album and track directories overlap with each other. export function sortByDirectory( data, - { getDirectory = (o) => o.directory } = {} + {getDirectory = (o) => o.directory} = {} ) { return data.sort((a, b) => { const ad = getDirectory(a); @@ -151,7 +151,7 @@ export function sortByDirectory( }); } -export function sortByName(data, { getName = (o) => o.name } = {}) { +export function sortByName(data, {getName = (o) => o.name} = {}) { return data.sort((a, b) => { const an = getName(a); const bn = getName(b); @@ -163,7 +163,7 @@ export function sortByName(data, { getName = (o) => o.name } = {}) { }); } -export function sortByDate(data, { getDate = (o) => o.date } = {}) { +export function sortByDate(data, {getDate = (o) => o.date} = {}) { return data.sort((a, b) => { const ad = getDate(a); const bd = getDate(b); @@ -254,9 +254,9 @@ export function sortByConditions(data, conditions) { // Expects thing properties: // * directory (or override getDirectory) // * name (or override getName) -export function sortAlphabetically(data, { getDirectory, getName } = {}) { - sortByDirectory(data, { getDirectory }); - sortByName(data, { getName }); +export function sortAlphabetically(data, {getDirectory, getName} = {}) { + sortByDirectory(data, {getDirectory}); + sortByName(data, {getName}); return data; } @@ -266,10 +266,10 @@ export function sortAlphabetically(data, { getDirectory, getName } = {}) { // * date (or override getDate) export function sortChronologically( data, - { getDirectory, getName, getDate } = {} + {getDirectory, getName, getDate} = {} ) { - sortAlphabetically(data, { getDirectory, getName }); - sortByDate(data, { getDate }); + sortAlphabetically(data, {getDirectory, getName}); + sortByDate(data, {getDate}); return data; } @@ -281,7 +281,7 @@ export function sortChronologically( // release date but can be overridden) above all else. // // This function also works for data lists which contain only tracks. -export function sortAlbumsTracksChronologically(data, { getDate } = {}) { +export function sortAlbumsTracksChronologically(data, {getDate} = {}) { // Sort albums before tracks... sortByConditions(data, [(t) => t.album === undefined]); @@ -297,7 +297,7 @@ export function sortAlbumsTracksChronologically(data, { getDate } = {}) { // released on the same date, they'll still be grouped together by album, // and tracks within an album will retain their relative positioning (i.e. // stay in the same order as part of the album's track listing). - sortByDate(data, { getDate }); + sortByDate(data, {getDate}); return data; } @@ -310,17 +310,17 @@ export function filterAlbumsByCommentary(albums) { ); } -export function getAlbumCover(album, { to }) { +export function getAlbumCover(album, {to}) { // Some albums don't have art! This function returns null in that case. if (album.hasCoverArt) { - return to("media.albumCover", album.directory, album.coverArtFileExtension); + return to('media.albumCover', album.directory, album.coverArtFileExtension); } else { return null; } } export function getAlbumListTag(album) { - return album.hasTrackNumbers ? "ol" : "ul"; + return album.hasTrackNumbers ? 'ol' : 'ul'; } // This gets all the track o8jects defined in every al8um, and sorts them 8y @@ -352,8 +352,8 @@ export function getArtistNumContributions(artist) { ); } -export function getFlashCover(flash, { to }) { - return to("media.flashArt", flash.directory, flash.coverArtFileExtension); +export function getFlashCover(flash, {to}) { + return to('media.flashArt', flash.directory, flash.coverArtFileExtension); } export function getFlashLink(flash) { @@ -364,16 +364,16 @@ export function getTotalDuration(tracks) { return tracks.reduce((duration, track) => duration + track.duration, 0); } -export function getTrackCover(track, { to }) { +export function getTrackCover(track, {to}) { // Some albums don't have any track art at all, and in those, every track // just inherits the album's own cover art. Note that since cover art isn't // guaranteed on albums either, it's possible that this function returns // null! if (!track.hasCoverArt) { - return getAlbumCover(track.album, { to }); + return getAlbumCover(track.album, {to}); } else { return to( - "media.trackCover", + 'media.trackCover', track.album.directory, track.directory, track.coverArtFileExtension @@ -381,14 +381,14 @@ export function getTrackCover(track, { to }) { } } -export function getArtistAvatar(artist, { to }) { - return to("media.artistAvatar", artist.directory, artist.avatarFileExtension); +export function getArtistAvatar(artist, {to}) { + return to('media.artistAvatar', artist.directory, artist.avatarFileExtension); } // Big-ass homepage row functions -export function getNewAdditions(numAlbums, { wikiData }) { - const { albumData } = wikiData; +export function getNewAdditions(numAlbums, {wikiData}) { + const {albumData} = wikiData; // Sort al8ums, in descending order of priority, 8y... // @@ -486,11 +486,11 @@ export function getNewAdditions(numAlbums, { wikiData }) { // Finally, do some quick mapping shenanigans to 8etter display the result // in a grid. (This should pro8a8ly 8e a separ8te, shared function, 8ut // whatevs.) - return albums.map((album) => ({ large: album.isMajorRelease, item: album })); + return albums.map((album) => ({large: album.isMajorRelease, item: album})); } -export function getNewReleases(numReleases, { wikiData }) { - const { albumData } = wikiData; +export function getNewReleases(numReleases, {wikiData}) { + const {albumData} = wikiData; const latestFirst = albumData .filter((album) => album.isListedOnHomepage) @@ -503,7 +503,7 @@ export function getNewReleases(numReleases, { wikiData }) { .slice(0, numReleases - majorReleases.length); return [ - ...majorReleases.map((album) => ({ large: true, item: album })), - ...otherReleases.map((album) => ({ large: false, item: album })), + ...majorReleases.map((album) => ({large: true, item: album})), + ...otherReleases.map((album) => ({large: false, item: album})), ]; } -- cgit 1.3.0-6-gf8a5