« get me outta code hell

hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/strings-default.yaml7
-rw-r--r--src/util/external-links.js98
-rw-r--r--tap-snapshots/test/snapshot/linkExternal.js.test.cjs18
-rw-r--r--test/snapshot/linkExternal.js7
4 files changed, 126 insertions, 4 deletions
diff --git a/src/strings-default.yaml b/src/strings-default.yaml
index 58f8401b..d194adde 100644
--- a/src/strings-default.yaml
+++ b/src/strings-default.yaml
@@ -505,6 +505,13 @@ misc:
 
     deviantart: "DeviantArt"
 
+    fandom:
+      _: "Fandom"
+
+      mspaintadventures:
+        _: "MSPA Wiki"
+        page: "MSPA Wiki ({PAGE})"
+
     homestuck:
       _: "Homestuck"
       page: "Homestuck (page {PAGE})"
diff --git a/src/util/external-links.js b/src/util/external-links.js
index 0b8b4ab6..57b6c3f6 100644
--- a/src/util/external-links.js
+++ b/src/util/external-links.js
@@ -6,6 +6,7 @@ import {
   isObject,
   isStringNonEmpty,
   optional,
+  validateAllPropertyValues,
   validateArrayItems,
   validateInstanceOf,
   validateProperties,
@@ -35,10 +36,24 @@ export const isExternalLinkContext = is(...externalLinkContexts);
 const isRegExp =
   validateInstanceOf(RegExp);
 
+export const isExternalLinkTransformCommand =
+  is(...[
+    'decode-uri',
+    'find-replace',
+  ]);
+
+export const isExternalLinkTransformSpec =
+  anyOf(
+    isExternalLinkTransformCommand,
+    validateProperties({
+      [validateProperties.allowOtherKeys]: true,
+      command: isExternalLinkTransformCommand,
+    }));
+
 export const isExternalLinkExtractSpec =
   validateProperties({
     prefix: optional(isStringNonEmpty),
-
+    transform: optional(validateArrayItems(isExternalLinkTransformSpec)),
     url: optional(isRegExp),
     domain: optional(isRegExp),
     pathname: optional(isRegExp),
@@ -79,7 +94,7 @@ export const isExternalLinkSpec =
       handle: optional(isExternalLinkExtractSpec),
 
       // TODO: This should validate each value with isExternalLinkExtractSpec.
-      custom: optional(isObject),
+      custom: optional(validateAllPropertyValues(isExternalLinkExtractSpec)),
     }));
 
 export const fallbackDescriptor = {
@@ -268,6 +283,44 @@ export const externalLinkSpec = [
   },
 
   {
+    match: {
+      domain: 'mspaintadventures.fandom.com',
+      pathname: /^wiki\/(.+)\/?$/,
+    },
+
+    platform: 'fandom',
+    substring: 'mspaintadventures.page',
+
+    normal: 'custom',
+    icon: 'globe',
+
+    custom: {
+      page: {
+        pathname: /^wiki\/(.+)\/?$/,
+        transform: [
+          {command: 'decode-uri'},
+          {command: 'find-replace', find: /_/g, replace: ' '},
+        ],
+      },
+    },
+  },
+
+  {
+    match: {domain: 'mspaintadventures.fandom.com'},
+
+    platform: 'fandom',
+    substring: 'mspaintadventures',
+
+    icon: 'globe',
+  },
+
+  {
+    match: {domain: 'fandom.com'},
+    platform: 'fandom',
+    icon: 'globe',
+  },
+
+  {
     match: {domain: 'homestuck.com'},
     platform: 'homestuck',
     icon: 'globe',
@@ -422,6 +475,7 @@ export function extractPartFromExternalLink(url, extract) {
 
   let regexen = [];
   let tests = [];
+  let transform = [];
   let prefix = '';
 
   if (extract instanceof RegExp) {
@@ -434,6 +488,32 @@ export function extractPartFromExternalLink(url, extract) {
           prefix = value;
           continue;
 
+        case 'transform':
+          for (const entry of value) {
+            const command =
+              (typeof entry === 'string'
+                ? command
+                : entry.command);
+
+            const options =
+              (typeof entry === 'string'
+                ? {}
+                : entry);
+
+            switch (command) {
+              case 'decode-uri':
+                transform.push(value =>
+                  decodeURIComponent(value));
+                break;
+
+              case 'find-replace':
+                transform.push(value =>
+                  value.replace(options.find, options.replace));
+                break;
+            }
+          }
+          continue;
+
         case 'url':
           tests.push(url);
           break;
@@ -459,17 +539,27 @@ export function extractPartFromExternalLink(url, extract) {
     }
   }
 
+  let value;
   for (const {regex, test} of stitchArrays({
     regex: regexen,
     test: tests,
   })) {
     const match = test.match(regex);
     if (match) {
-      return prefix + (match[1] ?? match[0]);
+      value = prefix + (match[1] ?? match[0]);
+      break;
     }
   }
 
-  return null;
+  if (!value) {
+    return null;
+  }
+
+  for (const fn of transform) {
+    value = fn(value);
+  }
+
+  return value;
 }
 
 export function extractAllCustomPartsFromExternalLink(url, custom) {
diff --git a/tap-snapshots/test/snapshot/linkExternal.js.test.cjs b/tap-snapshots/test/snapshot/linkExternal.js.test.cjs
index a9116be4..1327ba02 100644
--- a/tap-snapshots/test/snapshot/linkExternal.js.test.cjs
+++ b/tap-snapshots/test/snapshot/linkExternal.js.test.cjs
@@ -64,6 +64,12 @@ exports[`test/snapshot/linkExternal.js > TAP > linkExternal (snapshot) > context
 <a href="https://buzinkai.newgrounds.com/" class="nowrap">buzinkai.newgrounds.com</a>
 <a href="https://music.solatrus.com/" class="nowrap">music.solatrus.com</a>
 <a href="https://types.pl/" class="nowrap">types.pl</a>
+<a href="https://community.fandom.com/" class="nowrap">community.fandom.com</a>
+<a href="https://community.fandom.com/wiki/" class="nowrap">community.fandom.com</a>
+<a href="https://community.fandom.com/wiki/Community_Central" class="nowrap">community.fandom.com</a>
+<a href="https://mspaintadventures.fandom.com/" class="nowrap">mspaintadventures.fandom.com</a>
+<a href="https://mspaintadventures.fandom.com/wiki/" class="nowrap">mspaintadventures.fandom.com</a>
+<a href="https://mspaintadventures.fandom.com/wiki/Draconian_Dignitary" class="nowrap">mspaintadventures.fandom.com</a>
 `
 
 exports[`test/snapshot/linkExternal.js > TAP > linkExternal (snapshot) > context: generic, style: normal 1`] = `
@@ -80,6 +86,12 @@ exports[`test/snapshot/linkExternal.js > TAP > linkExternal (snapshot) > context
 <a href="https://buzinkai.newgrounds.com/" class="nowrap">Newgrounds</a>
 <a href="https://music.solatrus.com/" class="nowrap">Bandcamp (music.solatrus.com)</a>
 <a href="https://types.pl/" class="nowrap">Mastodon (types.pl)</a>
+<a href="https://community.fandom.com/" class="nowrap">Fandom</a>
+<a href="https://community.fandom.com/wiki/" class="nowrap">Fandom</a>
+<a href="https://community.fandom.com/wiki/Community_Central" class="nowrap">Fandom</a>
+<a href="https://mspaintadventures.fandom.com/" class="nowrap">MSPA Wiki</a>
+<a href="https://mspaintadventures.fandom.com/wiki/" class="nowrap">MSPA Wiki</a>
+<a href="https://mspaintadventures.fandom.com/wiki/Draconian_Dignitary" class="nowrap">MSPA Wiki (Draconian Dignitary)</a>
 `
 
 exports[`test/snapshot/linkExternal.js > TAP > linkExternal (snapshot) > context: generic, style: platform 1`] = `
@@ -96,6 +108,12 @@ exports[`test/snapshot/linkExternal.js > TAP > linkExternal (snapshot) > context
 <a href="https://buzinkai.newgrounds.com/" class="nowrap">Newgrounds</a>
 <a href="https://music.solatrus.com/" class="nowrap">Bandcamp</a>
 <a href="https://types.pl/" class="nowrap">Mastodon</a>
+<a href="https://community.fandom.com/" class="nowrap">Fandom</a>
+<a href="https://community.fandom.com/wiki/" class="nowrap">Fandom</a>
+<a href="https://community.fandom.com/wiki/Community_Central" class="nowrap">Fandom</a>
+<a href="https://mspaintadventures.fandom.com/" class="nowrap">Fandom</a>
+<a href="https://mspaintadventures.fandom.com/wiki/" class="nowrap">Fandom</a>
+<a href="https://mspaintadventures.fandom.com/wiki/Draconian_Dignitary" class="nowrap">Fandom</a>
 `
 
 exports[`test/snapshot/linkExternal.js > TAP > linkExternal (snapshot) > unknown domain (arbitrary world wide web path) 1`] = `
diff --git a/test/snapshot/linkExternal.js b/test/snapshot/linkExternal.js
index 434372a9..92cb8947 100644
--- a/test/snapshot/linkExternal.js
+++ b/test/snapshot/linkExternal.js
@@ -42,6 +42,13 @@ testContentFunctions(t, 'linkExternal (snapshot)', async (t, evaluate) => {
     // Just one custom domain of each platform is OK here
     'https://music.solatrus.com/',
     'https://types.pl/',
+
+    'https://community.fandom.com/',
+    'https://community.fandom.com/wiki/',
+    'https://community.fandom.com/wiki/Community_Central',
+    'https://mspaintadventures.fandom.com/',
+    'https://mspaintadventures.fandom.com/wiki/',
+    'https://mspaintadventures.fandom.com/wiki/Draconian_Dignitary',
   ]);
 
   quickSnapshotAllStyles('album', [