« get me outta code hell

hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
path: root/src/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/util')
-rw-r--r--src/util/cli.js8
-rw-r--r--src/util/html.js2
-rw-r--r--src/util/replacer.js1
-rw-r--r--src/util/sugar.js34
-rw-r--r--src/util/urls.js21
-rw-r--r--src/util/wiki-data.js70
6 files changed, 97 insertions, 39 deletions
diff --git a/src/util/cli.js b/src/util/cli.js
index e8c8c79f..4c08c085 100644
--- a/src/util/cli.js
+++ b/src/util/cli.js
@@ -17,7 +17,7 @@ export const ENABLE_COLOR =
 const C = (n) =>
   ENABLE_COLOR ? (text) => `\x1b[${n}m${text}\x1b[0m` : (text) => text;
 
-export const color = {
+export const colors = {
   bright: C('1'),
   dim: C('2'),
   normal: C('22'),
@@ -335,8 +335,8 @@ export function fileIssue({
   topMessage = `This shouldn't happen.`,
 } = {}) {
   if (topMessage) {
-    console.error(color.red(`${topMessage} Please let the HSMusic developers know:`));
+    console.error(colors.red(`${topMessage} Please let the HSMusic developers know:`));
   }
-  console.error(color.red(`- https://hsmusic.wiki/feedback/`));
-  console.error(color.red(`- https://github.com/hsmusic/hsmusic-wiki/issues/`));
+  console.error(colors.red(`- https://hsmusic.wiki/feedback/`));
+  console.error(colors.red(`- https://github.com/hsmusic/hsmusic-wiki/issues/`));
 }
diff --git a/src/util/html.js b/src/util/html.js
index f0c7bfdf..c7395fbf 100644
--- a/src/util/html.js
+++ b/src/util/html.js
@@ -242,7 +242,7 @@ export class Tag {
       this.selfClosing &&
       !(value === null ||
         value === undefined ||
-        !Boolean(value) ||
+        !value ||
         Array.isArray(value) && value.filter(Boolean).length === 0)
     ) {
       throw new Error(`Tag <${this.tagName}> is self-closing but got content`);
diff --git a/src/util/replacer.js b/src/util/replacer.js
index c5289cc5..647d1f0e 100644
--- a/src/util/replacer.js
+++ b/src/util/replacer.js
@@ -5,7 +5,6 @@
 // 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';
 
diff --git a/src/util/sugar.js b/src/util/sugar.js
index 5b1f3193..ebb7d61e 100644
--- a/src/util/sugar.js
+++ b/src/util/sugar.js
@@ -6,7 +6,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 {colors} 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
@@ -168,12 +168,24 @@ export function setIntersection(set1, set2) {
   return intersection;
 }
 
-export function filterProperties(obj, properties) {
-  const set = new Set(properties);
-  return Object.fromEntries(
-    Object
-      .entries(obj)
-      .filter(([key]) => set.has(key)));
+export function filterProperties(object, properties) {
+  if (typeof object !== 'object' || object === null) {
+    throw new TypeError(`Expected object to be an object, got ${object}`);
+  }
+
+  if (!Array.isArray(properties)) {
+    throw new TypeError(`Expected properties to be an array, got ${properties}`);
+  }
+
+  const filteredObject = {};
+
+  for (const property of properties) {
+    if (Object.hasOwn(object, property)) {
+      filteredObject[property] = object[property];
+    }
+  }
+
+  return filteredObject;
 }
 
 export function queue(array, max = 50) {
@@ -554,10 +566,10 @@ export function showAggregate(topError, {
             .trim()
             .replace(/file:\/\/.*\.js/, (match) => pathToFileURL(match))
         : '(no stack trace)';
-      header += ` ${color.dim(tracePart)}`;
+      header += ` ${colors.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' : colors.dim('\u254e');
+    const head = level % 2 === 0 ? '\u257f' : colors.dim('\u257f');
 
     if (error instanceof AggregateError) {
       return (
@@ -593,7 +605,7 @@ export function decorateErrorWithIndex(fn) {
     try {
       return fn(x, index, array);
     } catch (error) {
-      error.message = `(${color.yellow(`#${index + 1}`)}) ${error.message}`;
+      error.message = `(${colors.yellow(`#${index + 1}`)}) ${error.message}`;
       throw error;
     }
   };
diff --git a/src/util/urls.js b/src/util/urls.js
index d2b303e9..11b9b8b0 100644
--- a/src/util/urls.js
+++ b/src/util/urls.js
@@ -237,27 +237,6 @@ export function getPagePathname({
     : to('localized.' + pagePath[0], ...pagePath.slice(1)));
 }
 
-export function getPagePathnameAcrossLanguages({
-  defaultLanguage,
-  languages,
-  pagePath,
-  urls,
-}) {
-  return withEntries(languages, entries => entries
-    .filter(([key, language]) => key !== 'default' && !language.hidden)
-    .map(([_key, language]) => [
-      language.code,
-      getPagePathname({
-        baseDirectory:
-          (language === defaultLanguage
-            ? ''
-            : language.code),
-        pagePath,
-        urls,
-      }),
-    ]));
-}
-
 // Needed for the rare path arguments which themselves contains one or more
 // slashes, e.g. for listings, with arguments like 'albums/by-name'.
 export function getPageSubdirectoryPrefix({
diff --git a/src/util/wiki-data.js b/src/util/wiki-data.js
index ad2f82fb..ac652b27 100644
--- a/src/util/wiki-data.js
+++ b/src/util/wiki-data.js
@@ -1,6 +1,6 @@
 // Utility functions for interacting with wiki data.
 
-import {accumulateSum, empty, stitchArrays, unique} from './sugar.js';
+import {accumulateSum, empty, unique} from './sugar.js';
 
 // Generic value operations
 
@@ -874,3 +874,71 @@ export function filterItemsForCarousel(items) {
     .filter(item => item.artTags.every(tag => !tag.isContentWarning))
     .slice(0, maxCarouselLayoutItems + 1);
 }
+
+// Ridiculous caching support nonsense
+
+export class TupleMap {
+  static maxNestedTupleLength = 25;
+
+  #store = [undefined, null, null, null];
+
+  #lifetime(value) {
+    if (Array.isArray(value) && value.length <= TupleMap.maxNestedTupleLength) {
+      return 'tuple';
+    } else if (
+      typeof value === 'object' && value !== null ||
+      typeof value === 'function'
+    ) {
+      return 'weak';
+    } else {
+      return 'strong';
+    }
+  }
+
+  #getSubstoreShallow(value, store) {
+    const lifetime = this.#lifetime(value);
+    const mapIndex = {weak: 1, strong: 2, tuple: 3}[lifetime];
+
+    let map = store[mapIndex];
+    if (map === null) {
+      map = store[mapIndex] =
+        (lifetime === 'weak' ? new WeakMap()
+       : lifetime === 'strong' ? new Map()
+       : lifetime === 'tuple' ? new TupleMap()
+       : null);
+    }
+
+    if (map.has(value)) {
+      return map.get(value);
+    } else {
+      const substore = [undefined, null, null, null];
+      map.set(value, substore);
+      return substore;
+    }
+  }
+
+  #getSubstoreDeep(tuple, store = this.#store) {
+    if (tuple.length === 0) {
+      return store;
+    } else {
+      const [first, ...rest] = tuple;
+      return this.#getSubstoreDeep(rest, this.#getSubstoreShallow(first, store));
+    }
+  }
+
+  get(tuple) {
+    const store = this.#getSubstoreDeep(tuple);
+    return store[0];
+  }
+
+  has(tuple) {
+    const store = this.#getSubstoreDeep(tuple);
+    return store[0] !== undefined;
+  }
+
+  set(tuple, value) {
+    const store = this.#getSubstoreDeep(tuple);
+    store[0] = value;
+    return value;
+  }
+}