« 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/data/things/composite.js44
-rw-r--r--src/data/things/thing.js265
-rw-r--r--src/data/things/track.js45
3 files changed, 170 insertions, 184 deletions
diff --git a/src/data/things/composite.js b/src/data/things/composite.js
index f2ca2c7c..c33fc03c 100644
--- a/src/data/things/composite.js
+++ b/src/data/things/composite.js
@@ -1349,7 +1349,7 @@ export function exposeConstant({
 //            for values like zero and the empty string!
 //
 
-const availabilityCheckMode = {
+const availabilityCheckModeInput = {
   validate: oneOf('null', 'empty', 'falsy'),
   defaultValue: 'null',
 };
@@ -1359,7 +1359,7 @@ export const withResultOfAvailabilityCheck = templateCompositeFrom({
 
   inputs: {
     from: input(),
-    mode: input(availabilityCheckMode),
+    mode: input(availabilityCheckModeInput),
   },
 
   outputs: {
@@ -1403,7 +1403,7 @@ export const exposeDependencyOrContinue = templateCompositeFrom({
 
   inputs: {
     dependency: input(),
-    mode: input(availabilityCheckMode),
+    mode: input(availabilityCheckModeInput),
   },
 
   steps: () => [
@@ -1432,7 +1432,7 @@ export const exposeUpdateValueOrContinue = templateCompositeFrom({
   annotation: `exposeUpdateValueOrContinue`,
 
   inputs: {
-    mode: input(availabilityCheckMode),
+    mode: input(availabilityCheckModeInput),
   },
 
   steps: () => [
@@ -1450,7 +1450,7 @@ export const exitWithoutDependency = templateCompositeFrom({
 
   inputs: {
     dependency: input(),
-    mode: input(availabilityCheckMode),
+    mode: input(availabilityCheckModeInput),
     value: input({null: true}),
   },
 
@@ -1479,7 +1479,7 @@ export const exitWithoutUpdateValue = templateCompositeFrom({
   annotation: `exitWithoutUpdateValue`,
 
   inputs: {
-    mode: input(availabilityCheckMode),
+    mode: input(availabilityCheckModeInput),
     value: input({defaultValue: null}),
   },
 
@@ -1498,7 +1498,7 @@ export const raiseOutputWithoutDependency = templateCompositeFrom({
 
   inputs: {
     dependency: input(),
-    mode: input(availabilityCheckMode),
+    mode: input(availabilityCheckModeInput),
     output: input({defaultValue: {}}),
   },
 
@@ -1527,7 +1527,7 @@ export const raiseOutputWithoutUpdateValue = templateCompositeFrom({
   annotation: `raiseOutputWithoutUpdateValue`,
 
   inputs: {
-    mode: input(availabilityCheckMode),
+    mode: input(availabilityCheckModeInput),
     output: input({defaultValue: {}}),
   },
 
@@ -1562,19 +1562,21 @@ export const withPropertyFromObject = templateCompositeFrom({
   },
 
   outputs: {
-    into: {
-      dependencies: [
-        input.staticDependency('object'),
-        input.staticValue('property'),
-      ],
-
-      default: ({
-        [input.staticDependency('object')]: object,
-        [input.staticValue('property')]: property,
-      }) =>
-        (object.startsWith('#')
-          ? `${object}.${property}`
-          : `#${object}.${property}`),
+    dependencies: [
+      input.staticDependency('object'),
+      input.staticValue('property'),
+    ],
+
+    compute: ({
+      [input.staticDependency('object')]: object,
+      [input.staticValue('property')]: property,
+    }) => {
+      return (
+        (object && property
+          ? (object.startsWith('#')
+              ? `${object}.${property}`
+              : `#${object}.${property}`)
+          : '#value'));
     },
   },
 
diff --git a/src/data/things/thing.js b/src/data/things/thing.js
index 45e91238..a5f0b78d 100644
--- a/src/data/things/thing.js
+++ b/src/data/things/thing.js
@@ -5,7 +5,7 @@ import {inspect} from 'node:util';
 
 import {colors} from '#cli';
 import find from '#find';
-import {empty, stitchArrays, unique} from '#sugar';
+import {stitchArrays, unique} from '#sugar';
 import {filterMultipleArrays, getKebabCase} from '#wiki-data';
 import {oneOf} from '#validators';
 
@@ -253,6 +253,18 @@ export function additionalFiles() {
   };
 }
 
+const thingClassInput = {
+  validate(thingClass) {
+    isType(thingClass, 'function');
+
+    if (!Object.hasOwn(thingClass, Thing.referenceType)) {
+      throw new TypeError(`Expected a Thing constructor, missing Thing.referenceType`);
+    }
+
+    return true;
+  },
+};
+
 // A reference list! Keep in mind this is for general references to wiki
 // objects of (usually) other Thing subclasses, not specifically leitmotif
 // references in tracks (although that property uses referenceList too!).
@@ -267,18 +279,7 @@ export const referenceList = templateCompositeFrom({
   compose: false,
 
   inputs: {
-    class: input({
-      validate(thingClass) {
-        isType(thingClass, 'function');
-
-        if (!Object.hasOwn(thingClass, Thing.referenceType)) {
-          throw new TypeError(`Expected a Thing constructor, missing Thing.referenceType`);
-        }
-
-        return true;
-      },
-    }),
-
+    class: input(thingClassInput),
     find: input({type: 'function'}),
 
     // todo: validate
@@ -300,127 +301,100 @@ export const referenceList = templateCompositeFrom({
 
   steps: () => [
     withResolvedReferenceList({
-      list: '#updateValue',
-      data: '#composition.data',
-      find: '#composition.findFunction',
+      list: input.updateValue(),
+      data: input('data'),
+      find: input('find'),
     }),
 
     exposeDependency({dependency: '#resolvedReferenceList'}),
   ],
-})
-export function referenceList({
-  class: thingClass,
-  data,
-  find: findFunction,
-}) {
-  return compositeFrom({
-    annotation: `referenceList`,
-
-    mapDependencies: {
-      '#composition.data': data,
-    },
-
-    constantDependencies: {
-      '#composition.findFunction': findFunction,
-    },
-
-    steps: () => [
-      withUpdateValueAsDependency(),
-    ],
-  });
-}
+});
 
 // Corresponding function for a single reference.
-export function singleReference({
-  class: thingClass,
-  data,
-  find: findFunction,
-}) {
-  if (!thingClass) {
-    throw new TypeError(`Expected a Thing class`);
-  }
+export const singleReference = templateCompositeFrom({
+  annotation: `singleReference`,
 
-  const {[Thing.referenceType]: referenceType} = thingClass;
-  if (!referenceType) {
-    throw new Error(`The passed constructor ${thingClass.name} doesn't define Thing.referenceType!`);
-  }
+  compose: false,
 
-  return compositeFrom({
-    annotation: `singleReference`,
+  inputs: {
+    class: input(thingClassInput),
+    find: input({type: 'function'}),
 
-    update: {
-      validate: validateReference(referenceType),
-    },
+    // todo: validate
+    data: input(),
+  },
 
-    mapDependencies: {
-      '#composition.data': data,
-    },
+  update: {
+    dependencies: [
+      input.staticValue('class'),
+    ],
 
-    constantDependencies: {
-      '#composition.findFunction': findFunction,
+    compute({
+      [input.staticValue('class')]: thingClass,
+    }) {
+      const {[Thing.referenceType]: referenceType} = thingClass;
+      return {validate: validateReference(referenceType)};
     },
+  },
 
-    steps: () => [
-      withUpdateValueAsDependency(),
-
-      withResolvedReference({
-        ref: '#updateValue',
-        data: '#composition.data',
-        find: '#composition.findFunction',
-      }),
+  steps: () => [
+    withResolvedReference({
+      ref: input.updateValue(),
+      data: input('data'),
+      find: input('findFunction'),
+    }),
 
-      exposeDependency({dependency: '#resolvedReference'}),
-    ],
-  });
-}
+    exposeDependency({dependency: '#resolvedReference'}),
+  ],
+});
 
 // Nice 'n simple shorthand for an exposed-only flag which is true when any
 // contributions are present in the specified property.
-export function contribsPresent({
-  contribs,
-}) {
-  return compositeFrom({
-    annotation: `contribsPresent`,
+export const contribsPresent = templateCompositeFrom({
+  annotation: `contribsPresent`,
 
-    mapDependencies: {
-      '#composition.contribs': contribs,
-    },
+  compose: false,
 
-    steps: () => [
-      withResultOfAvailabilityCheck({
-        fromDependency: '#composition.contribs',
-        mode: 'empty',
-      }),
+  inputs: {
+    contribs: input({type: 'string'}),
+  },
 
-      exposeDependency({dependency: '#availability'}),
-    ],
-  });
-}
+  steps: () => [
+    withResultOfAvailabilityCheck({
+      fromDependency: input('contribs'),
+      mode: input.value('empty'),
+    }),
+
+    exposeDependency({dependency: '#availability'}),
+  ],
+});
 
 // Neat little shortcut for "reversing" the reference lists stored on other
 // things - for example, tracks specify a "referenced tracks" property, and
 // you would use this to compute a corresponding "referenced *by* tracks"
 // property. Naturally, the passed ref list property is of the things in the
 // wiki data provided, not the requesting Thing itself.
-export function reverseReferenceList({data, list}) {
-  return compositeFrom({
-    annotation: `reverseReferenceList`,
+export const reverseReferenceList = templateCompositeFrom({
+  annotation: `reverseReferenceList`,
 
-    mapDependencies: {
-      '#composition.data': data,
-      '#composition.list': list,
-    },
+  compose: false,
 
-    steps: () => [
-      withReverseReferenceList({
-        data: '#composition.data',
-        list: '#composition.list',
-      }),
+  inputs: {
+    // todo: validate
+    data: input(),
 
-      exposeDependency({dependency: '#reverseReferenceList'}),
-    ],
-  });
-}
+    list: input({type: 'string'}),
+  },
+
+  steps: () => [
+    withReverseReferenceList({
+      data: input('data'),
+      list: input('list'),
+    }),
+
+    exposeDependency({dependency: '#reverseReferenceList'}),
+  ],
+});
 
 // General purpose wiki data constructor, for properties like artistData,
 // trackData, etc.
@@ -436,53 +410,50 @@ export function wikiData(thingClass) {
 // This one's kinda tricky: it parses artist "references" from the
 // commentary content, and finds the matching artist for each reference.
 // This is mostly useful for credits and listings on artist pages.
-export function commentatorArtists() {
-  return compositeFrom({
-    annotation: `commentatorArtists`,
+export const commentatorArtists = templateCompositeFrom({
+  annotation: `commentatorArtists`,
+
+  compose: false,
+
+  steps: () => [
+    exitWithoutDependency({
+      dependency: 'commentary',
+      mode: input.value('falsy'),
+      value: input.value([]),
+    }),
 
-    constantDependencies: {
-      '#composition.findFunction': find.artists,
+    {
+      dependencies: ['commentary'],
+      compute: (continuation, {commentary}) =>
+        continuation({
+          '#artistRefs':
+            Array.from(
+              commentary
+                .replace(/<\/?b>/g, '')
+                .matchAll(/<i>(?<who>.*?):<\/i>/g))
+              .map(({groups: {who}}) => who),
+        }),
     },
 
-    steps: () => [
-      exitWithoutDependency({
-        dependency: 'commentary',
-        mode: 'falsy',
-        value: [],
-      }),
-
-      {
-        dependencies: ['commentary'],
-        compute: ({commentary}, continuation) =>
-          continuation({
-            '#artistRefs':
-              Array.from(
-                commentary
-                  .replace(/<\/?b>/g, '')
-                  .matchAll(/<i>(?<who>.*?):<\/i>/g))
-                .map(({groups: {who}}) => who),
-          }),
-      },
+    withResolvedReferenceList({
+      list: '#artistRefs',
+      data: 'artistData',
+      find: input.value(find.artist),
+    }).outputs({
+      '#resolvedReferenceList': '#artists',
+    }),
+
+    {
+      flags: {expose: true},
 
-      withResolvedReferenceList({
-        list: '#artistRefs',
-        data: 'artistData',
-        into: '#artists',
-        find: '#composition.findFunction',
-      }),
-
-      {
-        flags: {expose: true},
-
-        expose: {
-          dependencies: ['#artists'],
-          compute: ({'#artists': artists}) =>
-            unique(artists),
-        },
+      expose: {
+        dependencies: ['#artists'],
+        compute: ({'#artists': artists}) =>
+          unique(artists),
       },
-    ],
-  });
-}
+    },
+  ],
+});
 
 // Compositional utilities
 
diff --git a/src/data/things/track.js b/src/data/things/track.js
index 870b9913..b41dbb5b 100644
--- a/src/data/things/track.js
+++ b/src/data/things/track.js
@@ -140,9 +140,11 @@ export class Track extends Thing {
     artistContribs: [
       inheritFromOriginalRelease({property: 'artistContribs'}),
 
-      withResolvedContribs
-        .inputs({from: input.updateValue()})
-        .outputs({into: '#artistContribs'}),
+      withResolvedContribs({
+        from: input.updateValue(),
+      }).outputs({
+        '#resolvedContribs': '#artistContribs',
+      }),
 
       exposeDependencyOrContinue({dependency: '#artistContribs'}),
 
@@ -164,9 +166,11 @@ export class Track extends Thing {
     coverArtistContribs: [
       exitWithoutUniqueCoverArt(),
 
-      withResolvedContribs
-        .inputs({from: input.updateValue()})
-        .outputs({into: '#coverArtistContribs'}),
+      withResolvedContribs({
+        from: input.updateValue(),
+      }).outputs({
+        '#resolvedContribs': '#coverArtistContribs',
+      }),
 
       exposeDependencyOrContinue({dependency: '#coverArtistContribs'}),
 
@@ -400,7 +404,7 @@ export const withPropertyFromAlbum = templateCompositeFrom({
   annotation: `withPropertyFromAlbum`,
 
   inputs: {
-    property: input({type: 'string'}),
+    property: input.staticValue({type: 'string'}),
 
     notFoundMode: input({
       validate: oneOf('exit', 'null'),
@@ -409,12 +413,10 @@ export const withPropertyFromAlbum = templateCompositeFrom({
   },
 
   outputs: {
-    into: {
-      dependencies: [input.staticValue('property')],
-      default: ({
-        [input.staticValue('property')]: property,
-      }) => '#album.' + property,
-    },
+    dependencies: [input.staticValue('property')],
+    compute: ({
+      [input.staticValue('property')]: property,
+    }) => ['#album.' + property],
   },
 
   steps: () => [
@@ -422,9 +424,20 @@ export const withPropertyFromAlbum = templateCompositeFrom({
       notFoundMode: input('notFoundMode'),
     }),
 
-    withPropertyFromObject
-      .inputs({object: '#album', property: input('property')})
-      .outputs({into: 'into'}),
+    withPropertyFromObject({
+      object: '#album',
+      property: input('property'),
+    }),
+
+    {
+      dependencies: ['#value', input.staticValue('property')],
+      compute: (continuation, {
+        ['#value']: value,
+        [input.staticValue('property')]: property,
+      }) => continuation({
+        ['#album.' + property]: value,
+      }),
+    },
   ],
 });