From ef8acc5d50fa3c23bd7c9d4bb720b7ff78581981 Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Sat, 19 Aug 2023 14:13:31 -0300 Subject: clean up imports & miscellaneous metastructures across codebase --- src/content-function.js | 2 +- src/content/dependencies/generateAlbumInfoPage.js | 5 +- .../generateArtistGroupContributionsInfo.js | 7 +- src/content/dependencies/generateTrackInfoPage.js | 10 +- src/content/dependencies/index.js | 10 +- src/content/dependencies/listAlbumsByDuration.js | 8 +- src/content/dependencies/listAlbumsByTracks.js | 7 +- .../dependencies/listArtistsByCommentaryEntries.js | 6 +- .../dependencies/listArtistsByContributions.js | 7 +- src/content/dependencies/listArtistsByDuration.js | 8 +- src/content/dependencies/listArtistsByName.js | 6 +- src/content/dependencies/listGroupsByAlbums.js | 6 +- src/content/dependencies/listGroupsByDuration.js | 8 +- .../dependencies/listGroupsByLatestAlbum.js | 8 +- src/content/dependencies/listGroupsByTracks.js | 7 +- src/content/dependencies/listTagsByUses.js | 7 +- src/content/dependencies/listTracksByDate.js | 6 +- .../dependencies/listTracksByDurationInAlbum.js | 8 +- .../dependencies/listTracksByTimesReferenced.js | 7 +- src/content/dependencies/transformContent.js | 6 +- src/content/util/groupTracksByGroup.js | 2 +- src/data/language.js | 5 +- src/data/things/album.js | 6 +- src/data/things/art-tag.js | 6 +- src/data/things/artist.js | 4 +- src/data/things/cacheable-object.js | 4 +- src/data/things/flash.js | 4 +- src/data/things/group.js | 4 +- src/data/things/homepage-layout.js | 4 +- src/data/things/index.js | 15 +- src/data/things/thing.js | 16 +- src/data/things/track.js | 20 +- src/data/things/validators.js | 7 +- src/data/things/wiki-info.js | 4 +- src/data/yaml.js | 24 +- src/file-size-preloader.js | 5 +- src/find.js | 162 ++++++ src/gen-thumbs.js | 23 +- src/listing-spec.js | 8 +- src/page/album.js | 2 - src/page/artist-alias.js | 3 - src/page/artist.js | 7 +- src/page/flash.js | 5 +- src/page/group.js | 11 +- src/page/homepage.js | 13 - src/page/index.js | 9 - src/page/listing.js | 16 +- src/page/news.js | 2 - src/page/static.js | 7 +- src/repl.js | 37 +- src/static/client2.js | 3 - src/upd8.js | 93 +-- src/url-spec.js | 2 +- src/util/find.js | 162 ------ src/util/html.js | 8 +- src/util/node-utils.js | 6 +- src/util/replacer.js | 13 +- src/util/serialize.js | 8 + src/util/urls.js | 16 +- src/util/wiki-data.js | 7 +- src/write/bind-utilities.js | 74 +-- src/write/build-modes/live-dev-server.js | 41 +- src/write/build-modes/static-build.js | 78 +-- src/write/common-templates.js | 51 ++ src/write/page-template.js | 635 --------------------- src/write/validate-writes.js | 136 ----- 66 files changed, 487 insertions(+), 1420 deletions(-) create mode 100644 src/find.js delete mode 100644 src/util/find.js create mode 100644 src/write/common-templates.js delete mode 100644 src/write/page-template.js delete mode 100644 src/write/validate-writes.js (limited to 'src') diff --git a/src/content-function.js b/src/content-function.js index 443715dc..a3d7b585 100644 --- a/src/content-function.js +++ b/src/content-function.js @@ -3,7 +3,7 @@ import { decorateErrorWithCause, empty, setIntersection, -} from './util/sugar.js'; +} from '#sugar'; export class ContentFunctionSpecError extends Error {} diff --git a/src/content/dependencies/generateAlbumInfoPage.js b/src/content/dependencies/generateAlbumInfoPage.js index 992f817a..ce17ab21 100644 --- a/src/content/dependencies/generateAlbumInfoPage.js +++ b/src/content/dependencies/generateAlbumInfoPage.js @@ -1,6 +1,7 @@ -import getChronologyRelations from '../util/getChronologyRelations.js'; -import {sortAlbumsTracksChronologically} from '#wiki-data'; import {empty} from '#sugar'; +import {sortAlbumsTracksChronologically} from '#wiki-data'; + +import getChronologyRelations from '../util/getChronologyRelations.js'; export default { contentDependencies: [ diff --git a/src/content/dependencies/generateArtistGroupContributionsInfo.js b/src/content/dependencies/generateArtistGroupContributionsInfo.js index bc3992e2..1aa5dce6 100644 --- a/src/content/dependencies/generateArtistGroupContributionsInfo.js +++ b/src/content/dependencies/generateArtistGroupContributionsInfo.js @@ -1,9 +1,4 @@ -import { - empty, - filterProperties, - stitchArrays, - unique, -} from '#sugar'; +import {empty, filterProperties, stitchArrays, unique} from '#sugar'; export default { contentDependencies: ['linkGroup'], diff --git a/src/content/dependencies/generateTrackInfoPage.js b/src/content/dependencies/generateTrackInfoPage.js index 70106e30..334c5422 100644 --- a/src/content/dependencies/generateTrackInfoPage.js +++ b/src/content/dependencies/generateTrackInfoPage.js @@ -1,11 +1,7 @@ -import getChronologyRelations from '../util/getChronologyRelations.js'; - -import { - sortAlbumsTracksChronologically, - sortFlashesChronologically, -} from '#wiki-data'; - import {empty} from '#sugar'; +import {sortAlbumsTracksChronologically, sortFlashesChronologically} from '#wiki-data'; + +import getChronologyRelations from '../util/getChronologyRelations.js'; export default { contentDependencies: [ diff --git a/src/content/dependencies/index.js b/src/content/dependencies/index.js index 60408293..3bc34845 100644 --- a/src/content/dependencies/index.js +++ b/src/content/dependencies/index.js @@ -1,13 +1,13 @@ -import chokidar from 'chokidar'; -import {ESLint} from 'eslint'; - import EventEmitter from 'node:events'; import {readdir} from 'node:fs/promises'; import * as path from 'node:path'; import {fileURLToPath} from 'node:url'; -import contentFunction, {ContentFunctionSpecError} from '../../content-function.js'; -import {color, logWarn} from '../../util/cli.js'; +import chokidar from 'chokidar'; +import {ESLint} from 'eslint'; + +import {color, logWarn} from '#cli'; +import contentFunction, {ContentFunctionSpecError} from '#content-function'; import {annotateFunction} from '#sugar'; function cachebust(filePath) { diff --git a/src/content/dependencies/listAlbumsByDuration.js b/src/content/dependencies/listAlbumsByDuration.js index 73e78140..1f95f5e3 100644 --- a/src/content/dependencies/listAlbumsByDuration.js +++ b/src/content/dependencies/listAlbumsByDuration.js @@ -1,11 +1,5 @@ import {stitchArrays} from '#sugar'; - -import { - filterByCount, - getTotalDuration, - sortAlphabetically, - sortByCount, -} from '#wiki-data'; +import {filterByCount, getTotalDuration, sortAlphabetically, sortByCount} from '#wiki-data'; export default { contentDependencies: ['generateListingPage', 'linkAlbum'], diff --git a/src/content/dependencies/listAlbumsByTracks.js b/src/content/dependencies/listAlbumsByTracks.js index 1b925d2b..abf3c3ff 100644 --- a/src/content/dependencies/listAlbumsByTracks.js +++ b/src/content/dependencies/listAlbumsByTracks.js @@ -1,10 +1,5 @@ import {stitchArrays} from '#sugar'; - -import { - filterByCount, - sortAlphabetically, - sortByCount, -} from '#wiki-data'; +import {filterByCount, sortAlphabetically, sortByCount} from '#wiki-data'; export default { contentDependencies: ['generateListingPage', 'linkAlbum'], diff --git a/src/content/dependencies/listArtistsByCommentaryEntries.js b/src/content/dependencies/listArtistsByCommentaryEntries.js index a041e0bd..4db9885e 100644 --- a/src/content/dependencies/listArtistsByCommentaryEntries.js +++ b/src/content/dependencies/listArtistsByCommentaryEntries.js @@ -1,9 +1,5 @@ import {stitchArrays} from '#sugar'; -import { - filterByCount, - sortAlphabetically, - sortByCount, -} from '#wiki-data'; +import {filterByCount, sortAlphabetically, sortByCount} from '#wiki-data'; export default { contentDependencies: ['generateListingPage', 'linkArtist'], diff --git a/src/content/dependencies/listArtistsByContributions.js b/src/content/dependencies/listArtistsByContributions.js index ee876d58..86c8cfa2 100644 --- a/src/content/dependencies/listArtistsByContributions.js +++ b/src/content/dependencies/listArtistsByContributions.js @@ -1,10 +1,5 @@ import {stitchArrays, unique} from '#sugar'; - -import { - filterByCount, - sortAlphabetically, - sortByCount, -} from '#wiki-data'; +import {filterByCount, sortAlphabetically, sortByCount} from '#wiki-data'; export default { contentDependencies: ['generateListingPage', 'linkArtist'], diff --git a/src/content/dependencies/listArtistsByDuration.js b/src/content/dependencies/listArtistsByDuration.js index 5aeb8411..d6a18978 100644 --- a/src/content/dependencies/listArtistsByDuration.js +++ b/src/content/dependencies/listArtistsByDuration.js @@ -1,11 +1,5 @@ import {stitchArrays} from '#sugar'; - -import { - filterByCount, - getTotalDuration, - sortAlphabetically, - sortByCount, -} from '#wiki-data'; +import {filterByCount, getTotalDuration, sortAlphabetically, sortByCount} from '#wiki-data'; export default { contentDependencies: ['generateListingPage', 'linkArtist'], diff --git a/src/content/dependencies/listArtistsByName.js b/src/content/dependencies/listArtistsByName.js index 2c85bb4f..6c0ad836 100644 --- a/src/content/dependencies/listArtistsByName.js +++ b/src/content/dependencies/listArtistsByName.js @@ -1,9 +1,5 @@ import {stitchArrays} from '#sugar'; - -import { - getArtistNumContributions, - sortAlphabetically, -} from '#wiki-data'; +import {getArtistNumContributions, sortAlphabetically} from '#wiki-data'; export default { contentDependencies: ['generateListingPage', 'linkArtist'], diff --git a/src/content/dependencies/listGroupsByAlbums.js b/src/content/dependencies/listGroupsByAlbums.js index 83fe5ce7..063b8269 100644 --- a/src/content/dependencies/listGroupsByAlbums.js +++ b/src/content/dependencies/listGroupsByAlbums.js @@ -1,9 +1,5 @@ import {stitchArrays} from '#sugar'; -import { - filterByCount, - sortAlphabetically, - sortByCount, -} from '#wiki-data'; +import {filterByCount, sortAlphabetically, sortByCount} from '#wiki-data'; export default { contentDependencies: ['generateListingPage', 'linkGroup'], diff --git a/src/content/dependencies/listGroupsByDuration.js b/src/content/dependencies/listGroupsByDuration.js index dce4846e..e2a023e6 100644 --- a/src/content/dependencies/listGroupsByDuration.js +++ b/src/content/dependencies/listGroupsByDuration.js @@ -1,11 +1,5 @@ import {stitchArrays} from '#sugar'; - -import { - filterByCount, - getTotalDuration, - sortAlphabetically, - sortByCount, -} from '#wiki-data'; +import {filterByCount, getTotalDuration, sortAlphabetically, sortByCount} from '#wiki-data'; export default { contentDependencies: ['generateListingPage', 'linkGroup'], diff --git a/src/content/dependencies/listGroupsByLatestAlbum.js b/src/content/dependencies/listGroupsByLatestAlbum.js index 84012cf9..fa223664 100644 --- a/src/content/dependencies/listGroupsByLatestAlbum.js +++ b/src/content/dependencies/listGroupsByLatestAlbum.js @@ -1,11 +1,5 @@ import {stitchArrays} from '#sugar'; - -import { - compareDates, - filterMultipleArrays, - sortChronologically, - sortMultipleArrays, -} from '#wiki-data'; +import {compareDates, filterMultipleArrays, sortChronologically, sortMultipleArrays} from '#wiki-data'; export default { contentDependencies: [ diff --git a/src/content/dependencies/listGroupsByTracks.js b/src/content/dependencies/listGroupsByTracks.js index e105b991..b3c55ca2 100644 --- a/src/content/dependencies/listGroupsByTracks.js +++ b/src/content/dependencies/listGroupsByTracks.js @@ -1,10 +1,5 @@ import {accumulateSum, stitchArrays} from '#sugar'; - -import { - filterByCount, - sortAlphabetically, - sortByCount, -} from '#wiki-data'; +import {filterByCount, sortAlphabetically, sortByCount} from '#wiki-data'; export default { contentDependencies: ['generateListingPage', 'linkGroup'], diff --git a/src/content/dependencies/listTagsByUses.js b/src/content/dependencies/listTagsByUses.js index dae34048..98a50b89 100644 --- a/src/content/dependencies/listTagsByUses.js +++ b/src/content/dependencies/listTagsByUses.js @@ -1,10 +1,5 @@ import {stitchArrays} from '#sugar'; - -import { - filterByCount, - sortAlphabetically, - sortByCount, -} from '#wiki-data'; +import {filterByCount, sortAlphabetically, sortByCount} from '#wiki-data'; export default { contentDependencies: ['generateListingPage', 'linkArtTag'], diff --git a/src/content/dependencies/listTracksByDate.js b/src/content/dependencies/listTracksByDate.js index 94646822..d6546e67 100644 --- a/src/content/dependencies/listTracksByDate.js +++ b/src/content/dependencies/listTracksByDate.js @@ -1,9 +1,5 @@ import {stitchArrays} from '#sugar'; - -import { - chunkByProperties, - sortAlbumsTracksChronologically, -} from '#wiki-data'; +import {chunkByProperties, sortAlbumsTracksChronologically} from '#wiki-data'; export default { contentDependencies: ['generateListingPage', 'linkAlbum', 'linkTrack'], diff --git a/src/content/dependencies/listTracksByDurationInAlbum.js b/src/content/dependencies/listTracksByDurationInAlbum.js index db30ed06..4e83e921 100644 --- a/src/content/dependencies/listTracksByDurationInAlbum.js +++ b/src/content/dependencies/listTracksByDurationInAlbum.js @@ -1,11 +1,5 @@ import {stitchArrays} from '#sugar'; - -import { - filterByCount, - filterMultipleArrays, - sortByCount, - sortChronologically, -} from '#wiki-data'; +import {filterByCount, filterMultipleArrays, sortByCount, sortChronologically} from '#wiki-data'; export default { contentDependencies: ['generateListingPage', 'linkAlbum', 'linkTrack'], diff --git a/src/content/dependencies/listTracksByTimesReferenced.js b/src/content/dependencies/listTracksByTimesReferenced.js index 3cb5a350..15a3461d 100644 --- a/src/content/dependencies/listTracksByTimesReferenced.js +++ b/src/content/dependencies/listTracksByTimesReferenced.js @@ -1,10 +1,5 @@ import {stitchArrays} from '#sugar'; - -import { - filterByCount, - sortAlbumsTracksChronologically, - sortByCount, -} from '#wiki-data'; +import {filterByCount, sortAlbumsTracksChronologically, sortByCount} from '#wiki-data'; export default { contentDependencies: ['generateListingPage', 'linkTrack'], diff --git a/src/content/dependencies/transformContent.js b/src/content/dependencies/transformContent.js index 3d93284d..9a5ac456 100644 --- a/src/content/dependencies/transformContent.js +++ b/src/content/dependencies/transformContent.js @@ -1,7 +1,7 @@ -import {marked} from 'marked'; +import {bindFind} from '#find'; +import {parseInput} from '#replacer'; -import {bindFind} from '../../util/find.js'; -import {parseInput} from '../../util/replacer.js'; +import {marked} from 'marked'; export const replacerSpec = { album: { diff --git a/src/content/util/groupTracksByGroup.js b/src/content/util/groupTracksByGroup.js index 559967bc..4e189007 100644 --- a/src/content/util/groupTracksByGroup.js +++ b/src/content/util/groupTracksByGroup.js @@ -1,4 +1,4 @@ -import {empty} from '../../util/sugar.js'; +import {empty} from '#sugar'; export default function groupTracksByGroup(tracks, groups) { const lists = new Map(groups.map(group => [group, []])); diff --git a/src/data/language.js b/src/data/language.js index da9528f2..09466907 100644 --- a/src/data/language.js +++ b/src/data/language.js @@ -1,11 +1,10 @@ -import {readFile} from 'fs/promises'; +import {readFile} from 'node:fs/promises'; // It stands for "HTML Entities", apparently. Cursed. import he from 'he'; -import T from './things/index.js'; +import T from '#things'; -// TODO: define somewhere besides upd8.js obviously export async function processLanguageFile(file) { const contents = await readFile(file, 'utf-8'); const json = JSON.parse(contents); diff --git a/src/data/things/album.js b/src/data/things/album.js index 0c87b7c1..c012c243 100644 --- a/src/data/things/album.js +++ b/src/data/things/album.js @@ -1,7 +1,7 @@ -import Thing from './thing.js'; +import {empty} from '#sugar'; +import find from '#find'; -import {empty} from '../../util/sugar.js'; -import find from '../../util/find.js'; +import Thing from './thing.js'; export class Album extends Thing { static [Thing.referenceType] = 'album'; diff --git a/src/data/things/art-tag.js b/src/data/things/art-tag.js index 75fb0f7b..c103c4d5 100644 --- a/src/data/things/art-tag.js +++ b/src/data/things/art-tag.js @@ -1,8 +1,6 @@ -import Thing from './thing.js'; +import {sortAlbumsTracksChronologically} from '#wiki-data'; -import { - sortAlbumsTracksChronologically, -} from '../../util/wiki-data.js'; +import Thing from './thing.js'; export class ArtTag extends Thing { static [Thing.referenceType] = 'tag'; diff --git a/src/data/things/artist.js b/src/data/things/artist.js index f144b21f..522ca5f9 100644 --- a/src/data/things/artist.js +++ b/src/data/things/artist.js @@ -1,6 +1,6 @@ -import Thing from './thing.js'; +import find from '#find'; -import find from '../../util/find.js'; +import Thing from './thing.js'; export class Artist extends Thing { static [Thing.referenceType] = 'artist'; diff --git a/src/data/things/cacheable-object.js b/src/data/things/cacheable-object.js index 6a210cc1..ea705a61 100644 --- a/src/data/things/cacheable-object.js +++ b/src/data/things/cacheable-object.js @@ -74,9 +74,9 @@ // 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 {inspect as nodeInspect} from 'node:util'; -import {inspect as nodeInspect} from 'util'; +import {color, ENABLE_COLOR} from '#cli'; function inspect(value) { return nodeInspect(value, {colors: ENABLE_COLOR}); diff --git a/src/data/things/flash.js b/src/data/things/flash.js index 1383fa83..6eb5234f 100644 --- a/src/data/things/flash.js +++ b/src/data/things/flash.js @@ -1,6 +1,6 @@ -import Thing from './thing.js'; +import find from '#find'; -import find from '../../util/find.js'; +import Thing from './thing.js'; export class Flash extends Thing { static [Thing.referenceType] = 'flash'; diff --git a/src/data/things/group.js b/src/data/things/group.js index 111d6715..ba339b3e 100644 --- a/src/data/things/group.js +++ b/src/data/things/group.js @@ -1,6 +1,6 @@ -import Thing from './thing.js'; +import find from '#find'; -import find from '../../util/find.js'; +import Thing from './thing.js'; export class Group extends Thing { static [Thing.referenceType] = 'group'; diff --git a/src/data/things/homepage-layout.js b/src/data/things/homepage-layout.js index a79dd77a..ec9e9556 100644 --- a/src/data/things/homepage-layout.js +++ b/src/data/things/homepage-layout.js @@ -1,6 +1,6 @@ -import Thing from './thing.js'; +import find from '#find'; -import find from '../../util/find.js'; +import Thing from './thing.js'; export class HomepageLayout extends Thing { static [Thing.getPropertyDescriptors] = ({ diff --git a/src/data/things/index.js b/src/data/things/index.js index 11b6b1a9..591cdc3b 100644 --- a/src/data/things/index.js +++ b/src/data/things/index.js @@ -1,12 +1,12 @@ -import {logError} from '../../util/cli.js'; -import {openAggregate, showAggregate} from '../../util/sugar.js'; +import * as path from 'node:path'; +import {fileURLToPath} from 'node:url'; -import * as path from 'path'; -import {fileURLToPath} from 'url'; +import {logError} from '#cli'; +import * as serialize from '#serialize'; +import {openAggregate, showAggregate} from '#sugar'; +import * as validators from '#validators'; import Thing from './thing.js'; -import * as validators from './validators.js'; -import * as serialize from '../serialize.js'; import * as albumClasses from './album.js'; import * as artTagClasses from './art-tag.js'; @@ -20,6 +20,9 @@ import * as staticPageClasses from './static-page.js'; import * as trackClasses from './track.js'; import * as wikiInfoClasses from './wiki-info.js'; +export {default as Thing} from './thing.js'; +export {default as CacheableObject} from './cacheable-object.js'; + const allClassLists = { 'album.js': albumClasses, 'art-tag.js': artTagClasses, diff --git a/src/data/things/thing.js b/src/data/things/thing.js index 5004f4e6..c2876f56 100644 --- a/src/data/things/thing.js +++ b/src/data/things/thing.js @@ -1,7 +1,12 @@ // Thing: base class for wiki data types, providing wiki-specific utility // functions on top of essential CacheableObject behavior. -import CacheableObject from './cacheable-object.js'; +import {inspect} from 'node:util'; + +import {color} from '#cli'; +import find from '#find'; +import {empty} from '#sugar'; +import {getKebabCase} from '#wiki-data'; import { isAdditionalFileList, @@ -19,14 +24,9 @@ import { validateInstanceOf, validateReference, validateReferenceList, -} from './validators.js'; +} from '#validators'; -import {inspect} from 'util'; -import {color} from '../../util/cli.js'; -import {empty} from '../../util/sugar.js'; -import {getKebabCase} from '../../util/wiki-data.js'; - -import find from '../../util/find.js'; +import CacheableObject from './cacheable-object.js'; export default class Thing extends CacheableObject { static referenceType = Symbol('Thing.referenceType'); diff --git a/src/data/things/track.js b/src/data/things/track.js index 36e3adbe..e176acb4 100644 --- a/src/data/things/track.js +++ b/src/data/things/track.js @@ -1,10 +1,10 @@ -import Thing from './thing.js'; +import {inspect} from 'node:util'; -import {inspect} from 'util'; +import {color} from '#cli'; +import find from '#find'; +import {empty} from '#sugar'; -import {color} from '../../util/cli.js'; -import find from '../../util/find.js'; -import {empty} from '../../util/sugar.js'; +import Thing from './thing.js'; export class Track extends Thing { static [Thing.referenceType] = 'track'; @@ -47,7 +47,15 @@ export class Track extends Thing { hasCoverArt: { flags: {update: true, expose: true}, - update: {validate: isBoolean}, + update: { + validate(value) { + if (value !== false) { + throw new TypeError(`Expected false or null`); + } + + return true; + }, + }, expose: { dependencies: ['albumData', 'coverArtistContribsByRef'], diff --git a/src/data/things/validators.js b/src/data/things/validators.js index 1754adf3..fc953c2a 100644 --- a/src/data/things/validators.js +++ b/src/data/things/validators.js @@ -1,8 +1,7 @@ -import {withAggregate} from '../../util/sugar.js'; +import {inspect as nodeInspect} from 'node:util'; -import {color, ENABLE_COLOR} from '../../util/cli.js'; - -import {inspect as nodeInspect} from 'util'; +import {color, ENABLE_COLOR} from '#cli'; +import {withAggregate} from '#sugar'; function inspect(value) { return nodeInspect(value, {colors: ENABLE_COLOR}); diff --git a/src/data/things/wiki-info.js b/src/data/things/wiki-info.js index adf085e5..e906cab1 100644 --- a/src/data/things/wiki-info.js +++ b/src/data/things/wiki-info.js @@ -1,6 +1,6 @@ -import Thing from './thing.js'; +import find from '#find'; -import find from '../../util/find.js'; +import Thing from './thing.js'; export class WikiInfo extends Thing { static [Thing.getPropertyDescriptors] = ({ diff --git a/src/data/yaml.js b/src/data/yaml.js index c0058da3..35943199 100644 --- a/src/data/yaml.js +++ b/src/data/yaml.js @@ -1,15 +1,16 @@ // yaml.js - specification for HSMusic YAML data file format and utilities for -// loading and processing YAML files and documents +// loading, processing, and validating YAML files and documents -import * as path from 'path'; -import yaml from 'js-yaml'; - -import {readFile, stat} from 'fs/promises'; -import {inspect as nodeInspect} from 'util'; +import {readFile, stat} from 'node:fs/promises'; +import * as path from 'node:path'; +import {inspect as nodeInspect} from 'node:util'; -import T from './things/index.js'; +import yaml from 'js-yaml'; -import {color, ENABLE_COLOR, logInfo, logWarn} from '../util/cli.js'; +import {color, ENABLE_COLOR, logInfo, logWarn} from '#cli'; +import find, {bindFind} from '#find'; +import {traverse} from '#node-utils'; +import T from '#things'; import { conditionallySuppressError, @@ -20,17 +21,14 @@ import { openAggregate, showAggregate, withAggregate, -} from '../util/sugar.js'; +} from '#sugar'; import { sortAlbumsTracksChronologically, sortAlphabetically, sortChronologically, sortFlashesChronologically, -} from '../util/wiki-data.js'; - -import find, {bindFind} from '../util/find.js'; -import {traverse} from '../util/node-utils.js'; +} from '#wiki-data'; // --> General supporting stuff diff --git a/src/file-size-preloader.js b/src/file-size-preloader.js index ca1452d0..38e60e67 100644 --- a/src/file-size-preloader.js +++ b/src/file-size-preloader.js @@ -17,8 +17,9 @@ // 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 'node:fs/promises'; + +import {logWarn} from '#cli'; export default class FileSizePreloader { #paths = []; diff --git a/src/find.js b/src/find.js new file mode 100644 index 00000000..b8230800 --- /dev/null +++ b/src/find.js @@ -0,0 +1,162 @@ +import {inspect} from 'node:util'; + +import {color, logWarn} from '#cli'; + +function warnOrThrow(mode, message) { + if (mode === 'error') { + throw new Error(message); + } + + if (mode === 'warn') { + logWarn(message); + } + + return null; +} + +function findHelper(keys, findFns = {}) { + // Note: This cache explicitly *doesn't* support mutable data arrays. If the + // data array is modified, make sure it's actually a new array object, not + // the original, or the cache here will break and act as though the data + // hasn't changed! + const cache = new WeakMap(); + + const byDirectory = findFns.byDirectory || matchDirectory; + const byName = findFns.byName || matchName; + + const keyRefRegex = new RegExp(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'} = {}) => { + if (!fullRef) return null; + if (typeof fullRef !== 'string') { + throw new Error(`Got a reference that is ${typeof fullRef}, not string: ${fullRef}`); + } + + if (!data) { + throw new Error(`Expected data to be present`); + } + + if (!Array.isArray(data) && data.wikiData) { + throw new Error(`Old {wikiData: {...}} format provided`); + } + + let cacheForThisData = cache.get(data); + const cachedValue = cacheForThisData?.[fullRef]; + if (cachedValue) { + globalThis.NUM_CACHE = (globalThis.NUM_CACHE || 0) + 1; + return cachedValue; + } + if (!cacheForThisData) { + cacheForThisData = Object.create(null); + cache.set(data, cacheForThisData); + } + + const match = fullRef.match(keyRefRegex); + if (!match) { + return warnOrThrow(mode, `Malformed link reference: "${fullRef}"`); + } + + const key = match[1]; + const ref = match[2]; + + const found = key ? byDirectory(ref, data, mode) : byName(ref, data, mode); + + if (!found) { + warnOrThrow(mode, `Didn't match anything for ${color.bright(fullRef)}`); + } + + cacheForThisData[fullRef] = found; + + return found; + }; +} + +function matchDirectory(ref, data) { + return data.find(({directory}) => directory === ref); +} + +function matchName(ref, data, mode) { + const matches = data.filter( + ({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('') + + `Returning null for this reference.` + ); + } + + if (matches.length === 0) { + return null; + } + + const thing = matches[0]; + + if (ref !== thing.name) { + warnOrThrow( + mode, + `Bad capitalization: ${color.red(ref)} -> ${color.green(thing.name)}` + ); + } + + return thing; +} + +function matchTagName(ref, data, quiet) { + return matchName(ref.startsWith('cw: ') ? ref.slice(4) : ref, data, quiet); +} + +const find = { + album: findHelper(['album', 'album-commentary', 'album-gallery']), + 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; + +// Handy utility function for binding the find.thing() functions to a complete +// wikiData object, optionally taking default options to provide to the find +// function. Note that this caches the arrays read from wikiData right when it's +// called, so if their values change, you'll have to continue with a fresh call +// to bindFind. +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', + }).map(([key, value]) => { + const findFn = find[key]; + const thingData = wikiData[value]; + return [ + key, + opts1 + ? (ref, opts2) => + opts2 + ? findFn(ref, thingData, {...opts1, ...opts2}) + : findFn(ref, thingData, opts1) + : (ref, opts2) => + opts2 ? findFn(ref, thingData, opts2) : findFn(ref, thingData), + ]; + }) + ); +} diff --git a/src/gen-thumbs.js b/src/gen-thumbs.js index 82d5de3e..e9932822 100644 --- a/src/gen-thumbs.js +++ b/src/gen-thumbs.js @@ -85,18 +85,11 @@ const thumbnailSpec = { 'small': {size: 250, quality: 85}, }; -import {spawn} from 'child_process'; -import {createHash} from 'crypto'; -import * as path from 'path'; - -import { - readFile, - stat, - unlink, - writeFile, -} from 'fs/promises'; // Whatcha know! Nice. - -import {createReadStream} from 'fs'; // Still gotta import from 8oth tho, for createReadStream. +import {spawn} from 'node:child_process'; +import {createHash} from 'node:crypto'; +import {createReadStream} from 'node:fs'; +import {readFile, stat, unlink, writeFile} from 'node:fs/promises'; +import * as path from 'node:path'; import { color, @@ -106,16 +99,16 @@ import { logWarn, parseOptions, progressPromiseAll, -} from './util/cli.js'; +} from '#cli'; import { commandExists, isMain, promisifyProcess, traverse, -} from './util/node-utils.js'; +} from '#node-utils'; -import {delay, empty, queue} from './util/sugar.js'; +import {delay, empty, queue} from '#sugar'; export const defaultMagickThreads = 8; diff --git a/src/listing-spec.js b/src/listing-spec.js index 1e21baa1..fe36fc01 100644 --- a/src/listing-spec.js +++ b/src/listing-spec.js @@ -1,8 +1,4 @@ -import { - accumulateSum, - empty, - showAggregate, -} from './util/sugar.js'; +import {accumulateSum, empty, showAggregate} from '#sugar'; import { chunkByProperties, @@ -12,7 +8,7 @@ import { sortByDate, sortChronologically, sortFlashesChronologically, -} from './util/wiki-data.js'; +} from '#wiki-data'; const listingSpec = []; diff --git a/src/page/album.js b/src/page/album.js index 3b2d02d2..69fcabcf 100644 --- a/src/page/album.js +++ b/src/page/album.js @@ -1,5 +1,3 @@ -// Album page specification. - export const description = `per-album info, artwork gallery & commentary pages`; export function targets({wikiData}) { diff --git a/src/page/artist-alias.js b/src/page/artist-alias.js index 23513ce1..1da2af41 100644 --- a/src/page/artist-alias.js +++ b/src/page/artist-alias.js @@ -1,6 +1,3 @@ -// Artist alias redirect pages. -// (Makes old permalinks bring visitors to the up-to-date page.) - export const description = `redirects for aliased artist names`; export function targets({wikiData}) { diff --git a/src/page/artist.js b/src/page/artist.js index c53a4913..27ff8961 100644 --- a/src/page/artist.js +++ b/src/page/artist.js @@ -1,11 +1,8 @@ -// Artist page specification. -// -// NB: See artist-alias.js for artist alias redirect pages. - -import {empty} from '../util/sugar.js'; +import {empty} from '#sugar'; export const description = `per-artist info & artwork gallery pages`; +// NB: See artist-alias.js for artist alias redirect pages. export function targets({wikiData}) { return wikiData.artistData; } diff --git a/src/page/flash.js b/src/page/flash.js index 14e67d8d..b9d27d0f 100644 --- a/src/page/flash.js +++ b/src/page/flash.js @@ -1,7 +1,4 @@ -// Flash page and index specifications. - -import {empty} from '../util/sugar.js'; -import {getFlashLink} from '../util/wiki-data.js'; +import {empty} from '#sugar'; export const description = `flash & game pages`; diff --git a/src/page/group.js b/src/page/group.js index fa6c1c97..b0ed5baf 100644 --- a/src/page/group.js +++ b/src/page/group.js @@ -1,13 +1,4 @@ -// Group page specifications. - -import { - empty, -} from '../util/sugar.js'; - -import { - getTotalDuration, - sortChronologically, -} from '../util/wiki-data.js'; +import {empty} from '#sugar'; export const description = `per-group info & album gallery pages`; diff --git a/src/page/homepage.js b/src/page/homepage.js index 15dcadd1..53ee6e46 100644 --- a/src/page/homepage.js +++ b/src/page/homepage.js @@ -1,16 +1,3 @@ -// Homepage specification. - -import { - bindOpts, - empty, - withEntries, -} from '../util/sugar.js'; - -import { - getNewAdditions, - getNewReleases, -} from '../util/wiki-data.js'; - export const description = `main wiki homepage`; export function pathsTargetless({wikiData}) { diff --git a/src/page/index.js b/src/page/index.js index e62241d9..48e22d2e 100644 --- a/src/page/index.js +++ b/src/page/index.js @@ -1,12 +1,3 @@ -// 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. -// -// (TODO: The docs here from initial draft were totally outdated. -// We don't have docs for the new setup yet. -// Write those!!) -// - export * as album from './album.js'; export * as artist from './artist.js'; export * as artistAlias from './artist-alias.js'; diff --git a/src/page/listing.js b/src/page/listing.js index 64db413d..bb22c21f 100644 --- a/src/page/listing.js +++ b/src/page/listing.js @@ -1,19 +1,13 @@ -// Listing page specification. -// +export const description = `wiki-wide listing pages & index`; + // The targets here are a bit different than for most pages: rather than data // objects loaded from text files in the wiki data directory, they're hard- -// coded specifications, with various JS functions for processing wiki data -// and turning it into user-readable HTML listings. +// coded specifications, each directly identifying the hard-coded content +// function used to generate that listing. // // Individual listing specs are described in src/listing-spec.js, but are // provided via wikiData like other (normal) data objects. - -import {empty} from '../util/sugar.js'; - -import {getTotalDuration} from '../util/wiki-data.js'; - -export const description = `wiki-wide listing pages & index`; - +// export function targets({wikiData}) { return ( wikiData.listingSpec diff --git a/src/page/news.js b/src/page/news.js index 4928a116..194ffdcb 100644 --- a/src/page/news.js +++ b/src/page/news.js @@ -1,5 +1,3 @@ -// News entry & index page specifications. - export const description = `per-entry news pages & index`; export function condition({wikiData}) { diff --git a/src/page/static.js b/src/page/static.js index 82330dec..c9d806ff 100644 --- a/src/page/static.js +++ b/src/page/static.js @@ -1,9 +1,8 @@ -// 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.) - export const description = `static wiki-wide content pages specified in data`; +// Static pages are written in the wiki's data folder and contain content and +// basic page metadata. They're used for a variety of purposes, such as an +// "about" page, a changelog, links to places beyond the wiki, and so on. export function targets({wikiData}) { return wikiData.staticPageData; } diff --git a/src/repl.js b/src/repl.js index 6b6a405e..9ab4ddf0 100644 --- a/src/repl.js +++ b/src/repl.js @@ -1,23 +1,20 @@ -import * as os from 'os'; -import * as path from 'path'; -import * as repl from 'repl'; -import {fileURLToPath} from 'url'; - -import thingConstructors from './data/things/index.js'; -import {quickLoadAllFromYAML} from './data/yaml.js'; -import {logError, logWarn, parseOptions} from './util/cli.js'; -import {isMain} from './util/node-utils.js'; -import {bindOpts, showAggregate} from './util/sugar.js'; -import {generateURLs} from './util/urls.js'; - -import {processLanguageFile} from './data/language.js'; - -import * as serialize from './util/serialize.js'; -import * as sugar from './util/sugar.js'; -import * as wikiDataUtils from './util/wiki-data.js'; -import _find, {bindFind} from './util/find.js'; - -import urlSpec from './url-spec.js'; +import * as os from 'node:os'; +import * as path from 'node:path'; +import * as repl from 'node:repl'; +import {fileURLToPath} from 'node:url'; + +import {logError, logWarn, parseOptions} from '#cli'; +import {isMain} from '#node-utils'; +import {processLanguageFile} from '#language'; +import {bindOpts, showAggregate} from '#sugar'; +import {generateURLs, urlSpec} from '#urls'; +import {quickLoadAllFromYAML} from '#yaml'; + +import _find, {bindFind} from '#find'; +import thingConstructors from '#things'; +import * as serialize from '#serialize'; +import * as sugar from '#sugar'; +import * as wikiDataUtils from '#wiki-data'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); diff --git a/src/static/client2.js b/src/static/client2.js index 36dba2d1..0cdb8b0e 100644 --- a/src/static/client2.js +++ b/src/static/client2.js @@ -4,11 +4,8 @@ // 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 // ephemeral nature. -// -// Upd8: As of 04/02/2021, it's now used for info cards too! Nice. import {getColors} from '../util/colors.js'; - import {getArtistNumContributions} from '../util/wiki-data.js'; let albumData, artistData; diff --git a/src/upd8.js b/src/upd8.js index f5781272..bfdd1c2a 100755 --- a/src/upd8.js +++ b/src/upd8.js @@ -31,25 +31,37 @@ // 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 {execSync} from 'child_process'; -import * as path from 'path'; -import {fileURLToPath} from 'url'; +import {execSync} from 'node:child_process'; +import * as path from 'node:path'; +import {fileURLToPath} from 'node:url'; + import wrap from 'word-wrap'; +import {processLanguageFile} from '#language'; +import {isMain, traverse} from '#node-utils'; +import bootRepl from '#repl'; +import {empty, showAggregate, withEntries} from '#sugar'; +import {CacheableObject} from '#things'; +import {generateURLs, urlSpec} from '#urls'; +import {sortByName} from '#wiki-data'; + +import { + color, + decorateTime, + logWarn, + logInfo, + logError, + parseOptions, + progressCallAll, + progressPromiseAll, +} from '#cli'; + import genThumbs, { clearThumbs, defaultMagickThreads, isThumb, verifyImagePaths, -} from './gen-thumbs.js'; - -import bootRepl from './repl.js'; -import {listingSpec, listingTargetSpec} from './listing-spec.js'; -import urlSpec from './url-spec.js'; - -import {processLanguageFile} from './data/language.js'; - -import CacheableObject from './data/things/cacheable-object.js'; +} from '#thumbs'; import { filterDuplicateDirectories, @@ -58,28 +70,11 @@ import { loadAndProcessDataDocuments, sortWikiDataArrays, WIKI_INFO_FILE, -} from './data/yaml.js'; - -import {isMain, traverse} from './util/node-utils.js'; -import {empty, showAggregate, withEntries} from './util/sugar.js'; -import {generateURLs} from './util/urls.js'; -import {sortByName} from './util/wiki-data.js'; - -import {generateDevelopersCommentHTML} from './write/page-template.js'; -import * as buildModes from './write/build-modes/index.js'; - -import { - color, - decorateTime, - logWarn, - logInfo, - logError, - parseOptions, - progressCallAll, - progressPromiseAll, -} from './util/cli.js'; +} from '#yaml'; import FileSizePreloader from './file-size-preloader.js'; +import {listingSpec, listingTargetSpec} from './listing-spec.js'; +import * as buildModes from './write/build-modes/index.js'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); @@ -725,11 +720,35 @@ async function main() { if (noBuild) return; - const developersComment = generateDevelopersCommentHTML({ - buildTime: BUILD_TIME, - commit: COMMIT, - wikiData, - }); + const developersComment = + ``; return selectedBuildMode.go({ cliOptions, diff --git a/src/url-spec.js b/src/url-spec.js index 8340f196..4d103134 100644 --- a/src/url-spec.js +++ b/src/url-spec.js @@ -1,4 +1,4 @@ -import {withEntries} from './util/sugar.js'; +import {withEntries} from '#sugar'; const urlSpec = { data: { diff --git a/src/util/find.js b/src/util/find.js deleted file mode 100644 index dcb15b37..00000000 --- a/src/util/find.js +++ /dev/null @@ -1,162 +0,0 @@ -import {color, logWarn} from './cli.js'; - -import {inspect} from 'util'; - -function warnOrThrow(mode, message) { - if (mode === 'error') { - throw new Error(message); - } - - if (mode === 'warn') { - logWarn(message); - } - - return null; -} - -function findHelper(keys, findFns = {}) { - // Note: This cache explicitly *doesn't* support mutable data arrays. If the - // data array is modified, make sure it's actually a new array object, not - // the original, or the cache here will break and act as though the data - // hasn't changed! - const cache = new WeakMap(); - - const byDirectory = findFns.byDirectory || matchDirectory; - const byName = findFns.byName || matchName; - - const keyRefRegex = new RegExp(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'} = {}) => { - if (!fullRef) return null; - if (typeof fullRef !== 'string') { - throw new Error(`Got a reference that is ${typeof fullRef}, not string: ${fullRef}`); - } - - if (!data) { - throw new Error(`Expected data to be present`); - } - - if (!Array.isArray(data) && data.wikiData) { - throw new Error(`Old {wikiData: {...}} format provided`); - } - - let cacheForThisData = cache.get(data); - const cachedValue = cacheForThisData?.[fullRef]; - if (cachedValue) { - globalThis.NUM_CACHE = (globalThis.NUM_CACHE || 0) + 1; - return cachedValue; - } - if (!cacheForThisData) { - cacheForThisData = Object.create(null); - cache.set(data, cacheForThisData); - } - - const match = fullRef.match(keyRefRegex); - if (!match) { - return warnOrThrow(mode, `Malformed link reference: "${fullRef}"`); - } - - const key = match[1]; - const ref = match[2]; - - const found = key ? byDirectory(ref, data, mode) : byName(ref, data, mode); - - if (!found) { - warnOrThrow(mode, `Didn't match anything for ${color.bright(fullRef)}`); - } - - cacheForThisData[fullRef] = found; - - return found; - }; -} - -function matchDirectory(ref, data) { - return data.find(({directory}) => directory === ref); -} - -function matchName(ref, data, mode) { - const matches = data.filter( - ({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('') + - `Returning null for this reference.` - ); - } - - if (matches.length === 0) { - return null; - } - - const thing = matches[0]; - - if (ref !== thing.name) { - warnOrThrow( - mode, - `Bad capitalization: ${color.red(ref)} -> ${color.green(thing.name)}` - ); - } - - return thing; -} - -function matchTagName(ref, data, quiet) { - return matchName(ref.startsWith('cw: ') ? ref.slice(4) : ref, data, quiet); -} - -const find = { - album: findHelper(['album', 'album-commentary', 'album-gallery']), - 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; - -// Handy utility function for binding the find.thing() functions to a complete -// wikiData object, optionally taking default options to provide to the find -// function. Note that this caches the arrays read from wikiData right when it's -// called, so if their values change, you'll have to continue with a fresh call -// to bindFind. -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', - }).map(([key, value]) => { - const findFn = find[key]; - const thingData = wikiData[value]; - return [ - key, - opts1 - ? (ref, opts2) => - 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 5687bba3..7ed363c0 100644 --- a/src/util/html.js +++ b/src/util/html.js @@ -1,9 +1,9 @@ -// Some really simple functions for formatting HTML content. +// Some really, really simple functions for formatting HTML content. -import {inspect} from 'util'; +import {inspect} from 'node:util'; -import * as commonValidators from '../data/things/validators.js'; -import {empty} from './sugar.js'; +import {empty} from '#sugar'; +import * as commonValidators from '#validators'; // COMPREHENSIVE! // https://html.spec.whatwg.org/multipage/syntax.html#void-elements diff --git a/src/util/node-utils.js b/src/util/node-utils.js index 2fb7e8dd..345d10aa 100644 --- a/src/util/node-utils.js +++ b/src/util/node-utils.js @@ -1,8 +1,8 @@ // Utility functions which are only relevant to particular Node.js constructs. -import {readdir, stat} from 'fs/promises'; -import {fileURLToPath} from 'url'; -import * as path from 'path'; +import {readdir, stat} from 'node:fs/promises'; +import * as path from 'node:path'; +import {fileURLToPath} from 'node:url'; import _commandExists from 'command-exists'; diff --git a/src/util/replacer.js b/src/util/replacer.js index 278ffab5..c5289cc5 100644 --- a/src/util/replacer.js +++ b/src/util/replacer.js @@ -1,6 +1,13 @@ -import {logError, logWarn} from './cli.js'; -import {escapeRegex} from './sugar.js'; -import * as html from './html.js'; +// Regex-based forward parser for wiki content, breaking up text input into +// text and (possibly nested) tag nodes. +// +// The behavior here is quite tied into the `transformContent` content +// function, which converts nodes parsed here into actual HTML, links, etc +// for embedding in a wiki webpage. + +import {logError, logWarn} from '#cli'; +import * as html from '#html'; +import {escapeRegex} from '#sugar'; // Syntax literals. const tagBeginning = '[['; diff --git a/src/util/serialize.js b/src/util/serialize.js index 73a31374..4992e2bf 100644 --- a/src/util/serialize.js +++ b/src/util/serialize.js @@ -1,3 +1,10 @@ +// Utils used when per-wiki-object data files. +// Retained for reference and/or later reorganization. +// +// Not to be confused with data/serialize.js, which provides a generic +// interface for serializing any Thing object. + +/* export function serializeLink(thing) { const ret = {}; ret.name = thing.name; @@ -67,3 +74,4 @@ export function serializeGroupsForTrack(track, {serializeLink}) { urls: group.urls, })); } +*/ diff --git a/src/util/urls.js b/src/util/urls.js index 02961743..d2b303e9 100644 --- a/src/util/urls.js +++ b/src/util/urls.js @@ -4,9 +4,19 @@ // which can really quickly take su8stitute parameters to link from any one // place to another; 8ut there are also a few other utilities, too. -import * as path from 'path'; - -import {withEntries} from './sugar.js'; +import * as path from 'node:path'; + +import {withEntries} from '#sugar'; + +// This export is only provided for convenience, i.e. to enable the following: +// +// import {urlSpec} from '#urls'; +// +// It's not actually defined in this module's variable scope, and functions +// exported here require a urlSpec (whether this default one or another) to be +// passed directly. +// +export {default as urlSpec} from '../url-spec.js'; export function generateURLs(urlSpec) { const getValueForFullKey = (obj, fullKey) => { diff --git a/src/util/wiki-data.js b/src/util/wiki-data.js index 6930496f..ad2f82fb 100644 --- a/src/util/wiki-data.js +++ b/src/util/wiki-data.js @@ -1,11 +1,6 @@ // Utility functions for interacting with wiki data. -import { - accumulateSum, - empty, - stitchArrays, - unique, -} from './sugar.js'; +import {accumulateSum, empty, stitchArrays, unique} from './sugar.js'; // Generic value operations diff --git a/src/write/bind-utilities.js b/src/write/bind-utilities.js index 2ddc2b38..8e2adea7 100644 --- a/src/write/bind-utilities.js +++ b/src/write/bind-utilities.js @@ -4,12 +4,11 @@ import chroma from 'chroma-js'; -import * as html from '../util/html.js'; - -import {bindOpts} from '../util/sugar.js'; -import {getColors} from '../util/colors.js'; -import {bindFind} from '../util/find.js'; -import {thumb} from '../util/urls.js'; +import {getColors} from '#colors'; +import {bindFind} from '#find'; +import * as html from '#html'; +import {bindOpts} from '#sugar'; +import {thumb} from '#urls'; export function bindUtilities({ absoluteTo, @@ -24,9 +23,6 @@ export function bindUtilities({ urls, wikiData, }) { - // TODO: Is there some nicer way to define these, - // may8e without totally re-8inding everything for - // each page? const bound = {}; Object.assign(bound, { @@ -50,65 +46,5 @@ export function bindUtilities({ bound.find = bindFind(wikiData, {mode: 'warn'}); - /* - bound.generateNavigationLinks = bindOpts(generateNavigationLinks, { - link: bound.link, - language, - }); - - bound.generateStickyHeadingContainer = bindOpts(generateStickyHeadingContainer, { - [bindOpts.bindIndex]: 0, - html, - img: bound.img, - }); - - bound.generateChronologyLinks = bindOpts(generateChronologyLinks, { - html, - language, - link: bound.link, - wikiData, - - generateNavigationLinks: bound.generateNavigationLinks, - }); - - bound.generateInfoGalleryLinks = bindOpts(generateInfoGalleryLinks, { - [bindOpts.bindIndex]: 2, - link: bound.link, - language, - }); - - bound.getGridHTML = bindOpts(getGridHTML, { - [bindOpts.bindIndex]: 0, - img: bound.img, - html, - language, - - getRevealStringFromArtTags: bound.getRevealStringFromArtTags, - }); - - bound.getAlbumGridHTML = bindOpts(getAlbumGridHTML, { - [bindOpts.bindIndex]: 0, - link: bound.link, - language, - - getAlbumCover: bound.getAlbumCover, - getGridHTML: bound.getGridHTML, - }); - - bound.getFlashGridHTML = bindOpts(getFlashGridHTML, { - [bindOpts.bindIndex]: 0, - link: bound.link, - - getFlashCover: bound.getFlashCover, - getGridHTML: bound.getGridHTML, - }); - - bound.getCarouselHTML = bindOpts(getCarouselHTML, { - [bindOpts.bindIndex]: 0, - img: bound.img, - html, - }); - */ - return bound; } diff --git a/src/write/build-modes/live-dev-server.js b/src/write/build-modes/live-dev-server.js index edee3267..2767a02f 100644 --- a/src/write/build-modes/live-dev-server.js +++ b/src/write/build-modes/live-dev-server.js @@ -1,35 +1,26 @@ -import * as http from 'http'; -import {createReadStream} from 'fs'; -import {stat} from 'fs/promises'; -import * as path from 'path'; -import {pipeline} from 'stream/promises' +import * as http from 'node:http'; +import {createReadStream} from 'node:fs'; +import {stat} from 'node:fs/promises'; +import * as path from 'node:path'; +import {pipeline} from 'node:stream/promises' + +import {logInfo, logWarn, progressCallAll} from '#cli'; +import {watchContentDependencies} from '#content-dependencies'; +import {quickEvaluate} from '#content-function'; +import * as html from '#html'; +import * as pageSpecs from '#page-specs'; +import {serializeThings} from '#serialize'; +import {empty} from '#sugar'; -import {bindUtilities} from '../bind-utilities.js'; - -import {serializeThings} from '../../data/serialize.js'; - -import * as pageSpecs from '../../page/index.js'; - -import {logInfo, logWarn, progressCallAll} from '../../util/cli.js'; -import * as html from '../../util/html.js'; -import {empty} from '../../util/sugar.js'; import { getPagePathname, getPagePathnameAcrossLanguages, getURLsFrom, getURLsFromRoot, -} from '../../util/urls.js'; +} from '#urls'; -import { - generateGlobalWikiDataJSON, - generateRedirectHTML, -} from '../page-template.js'; - -import { - watchContentDependencies, -} from '../../content/dependencies/index.js'; - -import {quickEvaluate} from '../../content-function.js'; +import {bindUtilities} from '../bind-utilities.js'; +import {generateGlobalWikiDataJSON, generateRedirectHTML} from '../common-templates.js'; const defaultHost = '0.0.0.0'; const defaultPort = 8002; diff --git a/src/write/build-modes/static-build.js b/src/write/build-modes/static-build.js index 4f074058..2210dfe7 100644 --- a/src/write/build-modes/static-build.js +++ b/src/write/build-modes/static-build.js @@ -1,20 +1,20 @@ -import * as path from 'path'; - -import {bindUtilities} from '../bind-utilities.js'; -// import {validateWrites} from '../validate-writes.js'; +import * as path from 'node:path'; import { - quickLoadContentDependencies, -} from '../../content/dependencies/index.js'; - -import {quickEvaluate} from '../../content-function.js'; - -import {serializeThings} from '../../data/serialize.js'; - -import * as pageSpecs from '../../page/index.js'; + copyFile, + mkdir, + stat, + symlink, + writeFile, + unlink, +} from 'node:fs/promises'; -import * as html from '../../util/html.js'; -import {empty, queue, withEntries} from '../../util/sugar.js'; +import {quickLoadContentDependencies} from '#content-dependencies'; +import {quickEvaluate} from '#content-function'; +import * as html from '#html'; +import * as pageSpecs from '#page-specs'; +import {serializeThings} from '#serialize'; +import {empty, queue, withEntries} from '#sugar'; import { logError, @@ -22,14 +22,17 @@ import { logWarn, progressCallAll, progressPromiseAll, -} from '../../util/cli.js'; +} from '#cli'; import { getPagePathname, getPagePathnameAcrossLanguages, getURLsFrom, getURLsFromRoot, -} from '../../util/urls.js'; +} from '#urls'; + +import {bindUtilities} from '../bind-utilities.js'; +import {generateRedirectHTML, generateGlobalWikiDataJSON} from '../common-templates.js'; const pageFlags = Object.keys(pageSpecs); @@ -78,36 +81,6 @@ export function getCLIOptions() { }; } -function generateRedirectHTML(title, target, {language}) { - return `\n` + html.tag('html', [ - html.tag('head', [ - html.tag('title', language.$('redirectPage.title', {title})), - html.tag('meta', {charset: 'utf-8'}), - - html.tag('meta', { - 'http-equiv': 'refresh', - content: `0;url=${target}`, - }), - - // TODO: Is this OK for localized pages? - html.tag('link', { - rel: 'canonical', - href: target, - }), - ]), - - html.tag('body', - html.tag('main', [ - html.tag('h1', - language.$('redirectPage.title', {title})), - html.tag('p', - language.$('redirectPage.infoLine', { - target: html.tag('a', {href: target}, target), - })), - ])), - ]); -} - export async function go({ cliOptions, _dataPath, @@ -173,12 +146,10 @@ export async function go({ outputPath, urls, wikiData, - /* wikiDataJSON: generateGlobalWikiDataJSON({ serializeThings, wikiData, - }) - */ + }), }); const buildSteps = writeAll @@ -409,15 +380,6 @@ async function wrapLanguages(fn, { } } -import { - copyFile, - mkdir, - stat, - symlink, - writeFile, - unlink, -} from 'fs/promises'; - async function writePage({ pageHTML, oEmbedJSON = '', diff --git a/src/write/common-templates.js b/src/write/common-templates.js new file mode 100644 index 00000000..2dd4c924 --- /dev/null +++ b/src/write/common-templates.js @@ -0,0 +1,51 @@ +import * as html from '#html'; + +export function generateRedirectHTML(title, target, {language}) { + return `\n` + html.tag('html', [ + html.tag('head', [ + html.tag('title', language.$('redirectPage.title', {title})), + html.tag('meta', {charset: 'utf-8'}), + + html.tag('meta', { + 'http-equiv': 'refresh', + content: `0;url=${target}`, + }), + + // TODO: Is this OK for localized pages? + html.tag('link', { + rel: 'canonical', + href: target, + }), + ]), + + html.tag('body', + html.tag('main', [ + html.tag('h1', + language.$('redirectPage.title', {title})), + html.tag('p', + language.$('redirectPage.infoLine', { + target: html.tag('a', {href: target}, target), + })), + ])), + ]); +} + +export function generateGlobalWikiDataJSON({ + serializeThings, + wikiData, +}) { + const stringifyThings = thingData => + JSON.stringify(serializeThings(thingData)); + + return '{\n' + + ([ + `"albumData": ${stringifyThings(wikiData.albumData)},`, + wikiData.wikiInfo.enableFlashesAndGames && + `"flashData": ${stringifyThings(wikiData.flashData)},`, + `"artistData": ${stringifyThings(wikiData.artistData)}`, + ] + .filter(Boolean) + .map(line => ' ' + line) + .join('\n')) + + '\n}'; +} diff --git a/src/write/page-template.js b/src/write/page-template.js deleted file mode 100644 index d3d7b098..00000000 --- a/src/write/page-template.js +++ /dev/null @@ -1,635 +0,0 @@ -import chroma from 'chroma-js'; - -import * as html from '../util/html.js'; -import {getColors} from '../util/colors.js'; - -export function generateDevelopersCommentHTML({ - buildTime, - commit, - wikiData, -}) { - const {name, canonicalBase} = wikiData.wikiInfo; - return ``; -} - -export function generateDocumentHTML(pageInfo, { - cachebust, - defaultLanguage, - developersComment, - generateCoverLink, - generateStickyHeadingContainer, - img, - getThemeString, - language, - languages, - localizedPathnames, - oEmbedJSONHref, - pagePath, - pathname, - to, - transformMultiline, - wikiData, -}) { - const {wikiInfo} = wikiData; - - let { - title = '', - meta = {}, - theme = '', - stylesheet = '', - - showWikiNameInTitle = true, - themeColor = '', - - // missing properties are auto-filled, see below! - body = {}, - banner = {}, - cover = {}, - main = {}, - sidebarLeft = {}, - sidebarRight = {}, - nav = {}, - secondaryNav = {}, - footer = {}, - socialEmbed = {}, - } = pageInfo; - - body ||= {}; - body.style ??= ''; - - theme ||= getThemeString(wikiInfo.color); - - banner ||= {}; - banner.classes ??= []; - banner.src ??= ''; - banner.position ??= ''; - banner.dimensions ??= [0, 0]; - - main ||= {}; - main.classes ??= []; - main.content ??= ''; - main.headingMode ??= 'none'; - - cover ||= {}; - cover.src ??= ''; - cover.alt ??= ''; - cover.artTags ??= []; - - sidebarLeft ||= {}; - sidebarRight ||= {}; - - for (const sidebar of [sidebarLeft, sidebarRight]) { - sidebar.classes ??= []; - sidebar.content ??= ''; - sidebar.collapse ??= true; - } - - nav ||= {}; - nav.classes ??= []; - nav.content ??= ''; - nav.bottomRowContent ??= ''; - nav.links ??= []; - nav.linkContainerClasses ??= []; - - secondaryNav ||= {}; - secondaryNav.content ??= ''; - secondaryNav.content ??= ''; - - footer ||= {}; - footer.classes ??= []; - footer.content ??= wikiInfo.footerContent - ? transformMultiline(wikiInfo.footerContent) - : ''; - - socialEmbed ||= {}; - - const colors = themeColor - ? getColors(themeColor, {chroma}) - : null; - - const canonical = wikiInfo.canonicalBase - ? wikiInfo.canonicalBase + (pathname === '/' ? '' : pathname) - : ''; - - const localizedCanonical = wikiInfo.canonicalBase - ? Object.entries(localizedPathnames).map(([code, pathname]) => ({ - lang: code, - href: wikiInfo.canonicalBase + (pathname === '/' ? '' : pathname), - })) - : []; - - const collapseSidebars = - sidebarLeft.collapse !== false && sidebarRight.collapse !== false; - - - - const footerHTML = - html.tag('footer', - { - [html.onlyIfContent]: true, - id: 'footer', - class: footer.classes, - }, - [ - html.tag('div', - { - [html.onlyIfContent]: true, - class: 'footer-content', - }, - footer.content), - - getFooterLocalizationLinks({ - defaultLanguage, - html, - language, - languages, - pagePath, - to, - }), - ]); - - const generateSidebarHTML = (id, { - content, - multiple, - classes, - collapse = true, - wide = false, - - // 'last' - last or only sidebar box is sticky - // 'column' - entire column, incl. multiple boxes from top, is sticky - // 'none' - sidebar not sticky at all, stays at top of page - stickyMode = 'last', - }) => - content - ? html.tag('div', - { - id, - class: [ - 'sidebar-column', - 'sidebar', - wide && 'wide', - !collapse && 'no-hide', - stickyMode !== 'none' && 'sticky-' + stickyMode, - ...classes, - ], - }, - content) - : multiple - ? html.tag('div', - { - [html.onlyIfContent]: true, - id, - class: [ - 'sidebar-column', - 'sidebar-multiple', - wide && 'wide', - !collapse && 'no-hide', - stickyMode !== 'none' && 'sticky-' + stickyMode, - ], - }, - multiple - .filter(Boolean) - .map((infoOrContent) => - (typeof infoOrContent === 'object' && !Array.isArray(infoOrContent)) - ? infoOrContent - : {content: infoOrContent}) - .filter(({content}) => content) - .map(({ - content, - classes: classes2 = [], - }) => - html.tag('div', - { - class: ['sidebar', ...classes, ...classes2], - }, - html.fragment(content)))) - : ''; - - 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}]; - } - - const links = (nav.links || []).filter(Boolean); - - const navLinkParts = []; - for (let i = 0; i < links.length; i++) { - let cur = links[i]; - - let {title: linkTitle} = cur; - - if (cur.toHome) { - linkTitle ??= wikiInfo.nameShort; - } else if (cur.toCurrentPage) { - linkTitle ??= title; - } - - let partContent; - - if (typeof cur.html === 'string') { - partContent = cur.html; - } else { - const attributes = { - class: (cur.toCurrentPage || i === links.length - 1) && 'current', - href: cur.toCurrentPage - ? '' - : cur.toHome - ? to('localized.home') - : cur.path - ? to(...cur.path) - : null, - }; - if (attributes.href === null) { - throw new Error( - `Expected some href specifier for link to ${linkTitle} (${JSON.stringify( - cur - )})` - ); - } - partContent = html.tag('a', attributes, linkTitle); - } - - if (!partContent) continue; - - const part = html.tag('span', - {class: cur.divider === false && 'no-divider'}, - partContent); - - navLinkParts.push(part); - } - - const navHTML = html.tag('nav', - { - [html.onlyIfContent]: true, - id: 'header', - class: [ - ...nav.classes, - 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]}, - navLinkParts - ), - nav.bottomRowContent && - html.tag('div', {class: 'nav-bottom-row'}, nav.bottomRowContent), - nav.content && html.tag('div', {class: 'nav-content'}, nav.content), - ]); - - const secondaryNavHTML = html.tag('nav', - { - [html.onlyIfContent]: true, - id: 'secondary-nav', - class: secondaryNav.classes, - }, - secondaryNav.content); - - const bannerSrc = banner.src - ? banner.src - : banner.path - ? to(...banner.path) - : null; - - const bannerHTML = - banner.position && - bannerSrc && - html.tag('div', - { - id: 'banner', - class: banner.classes, - }, - html.tag('img', { - src: bannerSrc, - alt: banner.alt, - width: banner.dimensions[0] || 1100, - height: banner.dimensions[1] || 200, - })); - - const processSkippers = skipperList => - skipperList - .filter(Boolean) - .map(([href, stringSubkey]) => - html.tag('span', {class: 'skipper'}, - html.tag('a', - {href}, - language.$(`misc.skippers.${stringSubkey}`)))); - - // Hilariously jank. Sorry! - const hasID = id => mainHTML.includes(`id="${id}"`); - const hasTracks = hasID('tracks'); - const hasArt = hasID('art'); - const hasFlashes = hasID('flashes'); - const hasContributors = hasID('contributors'); - const hasReferences = hasID('references'); - const hasReferencedBy = hasID('referenced-by'); - const hasSamples = hasID('samples'); - const hasSampledBy = hasID('sampled-by'); - const hasFeatures = hasID('features'); - const hasFeaturedIn = hasID('featured-in'); - const hasLyrics = hasID('lyrics'); - const hasSheetMusicFiles = hasID('sheet-music-files'); - const hasMidiProjectFiles = hasID('midi-project-files'); - const hasAdditionalFiles = hasID('additional-files'); - const hasCommentary = hasID('commentary'); - const hasArtistCommentary = hasID('artist-commentary'); - - const skippersHTML = - mainHTML && - html.tag('div', {id: 'skippers'}, [ - html.tag('span', language.$('misc.skippers.skipTo')), - html.tag('div', {class: 'skipper-list'}, - processSkippers([ - ['#content', 'content'], - sidebarLeftHTML && - [ - '#sidebar-left', - sidebarRightHTML - ? 'sidebar.left' - : 'sidebar', - ], - sidebarRightHTML && - [ - '#sidebar-right', - sidebarLeftHTML - ? 'sidebar.right' - : 'sidebar', - ], - navHTML && - ['#header', 'header'], - footerHTML && - ['#footer', 'footer'], - ])), - - html.tag('div', - { - [html.onlyIfContent]: true, - class: 'skipper-list' - }, - processSkippers([ - hasTracks && - ['#tracks', 'tracks'], - hasArt && - ['#art', 'art'], - hasFlashes && - ['#flashes', 'flashes'], - hasContributors && - ['#contributors', 'contributors'], - hasReferences && - ['#references', 'references'], - hasReferencedBy && - ['#referenced-by', 'referencedBy'], - hasSamples && - ['#samples', 'samples'], - hasSampledBy && - ['#sampled-by', 'sampledBy'], - hasFeatures && - ['#features', 'features'], - hasFeaturedIn && - ['#featured-in', 'featuredIn'], - hasLyrics && - ['#lyrics', 'lyrics'], - hasSheetMusicFiles && - ['#sheet-music-files', 'sheetMusicFiles'], - hasMidiProjectFiles && - ['#midi-project-files', 'midiProjectFiles'], - hasAdditionalFiles && - ['#additional-files', 'additionalFiles'], - hasCommentary && - ['#commentary', 'commentary'], - hasArtistCommentary && - ['#artist-commentary', 'artistCommentary'], - ])), - ]); - - const infoCardHTML = html.tag('div', {id: 'info-card-container'}, - html.tag('div', {id: 'info-card-decor'}, - html.tag('div', {id: 'info-card'}, [ - html.tag('div', {class: ['info-card-art-container', 'no-reveal']}, - img({ - class: 'info-card-art', - src: '', - link: true, - square: true, - })), - html.tag('div', {class: ['info-card-art-container', 'reveal']}, - img({ - class: 'info-card-art', - src: '', - link: true, - square: true, - reveal: getRevealStringFromContentWarningMessage( - html.tag('span', {class: 'info-card-art-warnings'}), - {html, language}), - })), - html.tag('h1', {class: 'info-card-name'}, - html.tag('a')), - html.tag('p', {class: 'info-card-album'}, - language.$('releaseInfo.from', { - album: html.tag('a'), - })), - html.tag('p', {class: 'info-card-artists'}, - language.$('releaseInfo.by', { - artists: html.tag('span'), - })), - html.tag('p', {class: 'info-card-cover-artists'}, - language.$('releaseInfo.coverArtBy', { - artists: html.tag('span'), - })), - ]))); - - const imageOverlayHTML = html.tag('div', {id: 'image-overlay-container'}, - html.tag('div', {id: 'image-overlay-content-container'}, [ - html.tag('a', {id: 'image-overlay-image-container'}, [ - html.tag('img', {id: 'image-overlay-image'}), - html.tag('img', {id: 'image-overlay-image-thumb'}), - ]), - html.tag('div', {id: 'image-overlay-action-container'}, [ - html.tag('div', {id: 'image-overlay-action-content-without-size'}, - language.$('releaseInfo.viewOriginalFile', { - link: html.tag('a', {class: 'image-overlay-view-original'}, - language.$('releaseInfo.viewOriginalFile.link')), - })), - - html.tag('div', {id: 'image-overlay-action-content-with-size'}, [ - language.$('releaseInfo.viewOriginalFile.withSize', { - link: html.tag('a', {class: 'image-overlay-view-original'}, - language.$('releaseInfo.viewOriginalFile.link')), - size: html.tag('span', - {[html.joinChildren]: ''}, - [ - html.tag('span', {id: 'image-overlay-file-size-kilobytes'}, - language.$('count.fileSize.kilobytes', { - kilobytes: html.tag('span', {class: 'image-overlay-file-size-count'}), - })), - html.tag('span', {id: 'image-overlay-file-size-megabytes'}, - language.$('count.fileSize.megabytes', { - megabytes: html.tag('span', {class: 'image-overlay-file-size-count'}), - })), - ]), - }), - - html.tag('span', {id: 'image-overlay-file-size-warning'}, - language.$('releaseInfo.viewOriginalFile.sizeWarning')), - ]), - ]), - ])); - - const socialEmbedHTML = [ - socialEmbed.title && - html.tag('meta', {property: 'og:title', content: socialEmbed.title}), - - socialEmbed.description && - html.tag('meta', { - property: 'og:description', - content: socialEmbed.description, - }), - - socialEmbed.image && - html.tag('meta', {property: 'og:image', content: socialEmbed.image}), - - ...html.fragment( - colors && [ - html.tag('meta', { - name: 'theme-color', - content: colors.dark, - media: '(prefers-color-scheme: dark)', - }), - - html.tag('meta', { - name: 'theme-color', - content: colors.light, - media: '(prefers-color-scheme: light)', - }), - - html.tag('meta', { - name: 'theme-color', - content: colors.primary, - }), - ]), - - oEmbedJSONHref && - html.tag('link', { - type: 'application/json+oembed', - href: oEmbedJSONHref, - }), - ].filter(Boolean).join('\n'); - - return `\n` -} - -export function generateOEmbedJSON(pageInfo, {language, wikiData}) { - const {socialEmbed} = pageInfo; - const {wikiInfo} = wikiData; - const {canonicalBase, nameShort} = wikiInfo; - - if (!socialEmbed) return ''; - - const entries = [ - socialEmbed.heading && [ - 'author_name', - language.$('misc.socialEmbed.heading', { - wikiName: nameShort, - heading: socialEmbed.heading, - }), - ], - socialEmbed.headingLink && - canonicalBase && [ - 'author_url', - canonicalBase.replace(/\/$/, '') + - '/' + - socialEmbed.headingLink.replace(/^\//, ''), - ], - ].filter(Boolean); - - if (!entries.length) return ''; - - return JSON.stringify(Object.fromEntries(entries)); -} - -export function generateRedirectHTML(title, target, { - language, -}) { - return `\n` + html.tag('html', [ - html.tag('head', [ - html.tag('title', language.$('redirectPage.title', {title})), - html.tag('meta', {charset: 'utf-8'}), - - html.tag('meta', { - 'http-equiv': 'refresh', - content: `0;url=${target}`, - }), - - // TODO: Is this OK for localized pages? - html.tag('link', { - rel: 'canonical', - href: target, - }), - ]), - - html.tag('body', - html.tag('main', [ - html.tag('h1', - language.$('redirectPage.title', {title})), - html.tag('p', - language.$('redirectPage.infoLine', { - target: html.tag('a', {href: target}, target), - })), - ])), - ]); -} - -export function generateGlobalWikiDataJSON({ - serializeThings, - wikiData, -}) { - return '{\n' + - ([ - `"albumData": ${stringifyThings(wikiData.albumData)},`, - wikiData.wikiInfo.enableFlashesAndGames && - `"flashData": ${stringifyThings(wikiData.flashData)},`, - `"artistData": ${stringifyThings(wikiData.artistData)}`, - ] - .filter(Boolean) - .map(line => ' ' + line) - .join('\n')) + - '\n}'; - - function stringifyThings(thingData) { - return JSON.stringify(serializeThings(thingData)); - } -} diff --git a/src/write/validate-writes.js b/src/write/validate-writes.js deleted file mode 100644 index 52c7dfab..00000000 --- a/src/write/validate-writes.js +++ /dev/null @@ -1,136 +0,0 @@ -// TODO: All this is for an outdated spec + should use aggregate errors - -import {logError} from '../util/cli.js'; - -function validateWritePath(path, urlGroup) { - if (!Array.isArray(path)) { - return {error: `Expected array, got ${path}`}; - } - - const {paths} = urlGroup; - - const definedKeys = Object.keys(paths); - const specifiedKey = path[0]; - - if (!definedKeys.includes(specifiedKey)) { - return {error: `Specified key ${specifiedKey} isn't defined`}; - } - - const expectedArgs = paths[specifiedKey].match(/<>/g)?.length ?? 0; - const specifiedArgs = path.length - 1; - - if (specifiedArgs !== expectedArgs) { - return { - error: `Expected ${expectedArgs} arguments, got ${specifiedArgs}`, - }; - } - - return {success: true}; -} - -function validateWriteObject(obj, { - urlSpec, -}) { - 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}`}; - } - - switch (obj.type) { - case 'legacy': { - if (typeof obj.write !== 'function') { - return {error: `Expected write to be string, got ${obj.write}`}; - } - - break; - } - - case 'page': { - const path = validateWritePath(obj.path, urlSpec.localized); - if (path.error) { - return {error: `Path validation failed: ${path.error}`}; - } - - if (typeof obj.page !== 'function') { - return {error: `Expected page to be function, got ${obj.content}`}; - } - - break; - } - - case 'data': { - const path = validateWritePath(obj.path, urlSpec.data); - if (path.error) { - return {error: `Path validation failed: ${path.error}`}; - } - - if (typeof obj.data !== 'function') { - return {error: `Expected data to be function, got ${obj.data}`}; - } - - break; - } - - case 'redirect': { - const fromPath = validateWritePath(obj.fromPath, urlSpec.localized); - if (fromPath.error) { - return { - error: `Path (fromPath) validation failed: ${fromPath.error}`, - }; - } - - const toPath = validateWritePath(obj.toPath, urlSpec.localized); - if (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}`}; - } - - break; - } - - default: { - return {error: `Unknown type: ${obj.type}`}; - } - } - - return {success: true}; -} - -export function validateWrites(writes, { - functionName, - urlSpec, -}) { - // Do a quick valid8tion! If one of the writeThingPages functions go - // wrong, this will stall out early and tell us which did. - - if (!Array.isArray(writes)) { - logError`${functionName} didn't return an array!`; - return false; - } - - if (!( - writes.every((obj) => typeof obj === 'object') && - writes.every((obj) => { - const result = validateWriteObject(obj, { - urlSpec, - }); - if (result.error) { - logError`Validating write object failed: ${result.error}`; - return false; - } else { - return true; - } - }) - )) { - logError`${functionName} returned invalid entries!`; - return false; - } - - return true; -} -- cgit 1.3.0-6-gf8a5