« get me outta code hell

hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
path: root/src/data
diff options
context:
space:
mode:
Diffstat (limited to 'src/data')
-rw-r--r--src/data/checks.js7
-rw-r--r--src/data/composite.js32
-rw-r--r--src/data/composite/things/content/withExpressedOrImplicitArtistReferences.js29
-rw-r--r--src/data/things/content/ContentEntry.js53
4 files changed, 91 insertions, 30 deletions
diff --git a/src/data/checks.js b/src/data/checks.js
index 0a0e7f52..01b5cf9e 100644
--- a/src/data/checks.js
+++ b/src/data/checks.js
@@ -352,6 +352,9 @@ export function filterReferenceErrors(wikiData, {
 
             switch (findFnKey) {
               case '_content':
+                // note: quoted artists ("Quoted Artists" field)`) currently
+                // not handled here at all. limitation of current code! oops!
+
                 if (value) {
                   value =
                     value.map(entry =>
@@ -721,13 +724,13 @@ export function reportContentTextErrors(wikiData, {
 
   const commentaryShape = {
     body: 'commentary body',
-    artistText: 'commentary artist text',
+    headingArtistText: 'commentary artist text',
     annotation: 'commentary annotation',
   };
 
   const lyricsShape = {
     body: 'lyrics body',
-    artistText: 'lyrics artist text',
+    headingArtistText: 'lyrics artist text',
     annotation: 'lyrics annotation',
   };
 
diff --git a/src/data/composite.js b/src/data/composite.js
index 8ac906c7..3b462ef5 100644
--- a/src/data/composite.js
+++ b/src/data/composite.js
@@ -10,17 +10,27 @@ import {TupleMap} from '#wiki-data';
 
 const globalCompositeCache = {};
 
-const _valueIntoToken = shape =>
-  (value = null) =>
-    (value === null
-      ? Symbol.for(`hsmusic.composite.${shape}`)
-   : typeof value === 'string'
-      ? Symbol.for(`hsmusic.composite.${shape}:${value}`)
-      : {
-          symbol: Symbol.for(`hsmusic.composite.${shape.split('.')[0]}`),
-          shape,
-          value,
-        });
+const _valueIntoToken = shape => (value = null) => {
+  if (value === null) {
+    return Symbol.for(`hsmusic.composite.${shape}`);
+  }
+
+  if (typeof value === 'string') {
+    return Symbol.for(`hsmusic.composite.${shape}:${value}`);
+  }
+
+  if (typeof value === 'object') {
+    if (Object.values(value).some(isInputToken)) {
+      throw new TypeError(`Don't nest input tokens inside ${shape}()`);
+    }
+  }
+
+  return {
+    symbol: Symbol.for(`hsmusic.composite.${shape.split('.')[0]}`),
+    shape,
+    value,
+  };
+};
 
 export const input = _valueIntoToken('input');
 input.symbol = Symbol.for('hsmusic.composite.input');
diff --git a/src/data/composite/things/content/withExpressedOrImplicitArtistReferences.js b/src/data/composite/things/content/withExpressedOrImplicitArtistReferences.js
index 69da8c75..a6200ee8 100644
--- a/src/data/composite/things/content/withExpressedOrImplicitArtistReferences.js
+++ b/src/data/composite/things/content/withExpressedOrImplicitArtistReferences.js
@@ -8,16 +8,19 @@ export default templateCompositeFrom({
   annotation: `withExpressedOrImplicitArtistReferences`,
 
   inputs: {
-    from: input({type: 'array', acceptsNull: true}),
+    fromExpressed: input({type: 'array', acceptsNull: true}),
+    fromContent: input({type: 'string', acceptsNull: true}),
+
+    filterArtistTags: input({type: 'function', defaultValue: () => true}),
   },
 
   outputs: ['#artistReferences'],
 
   steps: () => [
     {
-      dependencies: [input('from')],
+      dependencies: [input('fromExpressed')],
       compute: (continuation, {
-        [input('from')]: expressedArtistReferences,
+        [input('fromExpressed')]: expressedArtistReferences,
       }) =>
         (expressedArtistReferences
           ? continuation.raiseOutput({'#artistReferences': expressedArtistReferences})
@@ -25,12 +28,12 @@ export default templateCompositeFrom({
     },
 
     raiseOutputWithoutDependency({
-      dependency: 'artistText',
-      output: input.value({'#artistReferences': null}),
+      dependency: input('fromContent'),
+      output: input.value({'#artistReferences': []}),
     }),
 
     withContentNodes({
-      from: 'artistText',
+      from: input('fromContent'),
     }),
 
     withMappedList({
@@ -42,13 +45,19 @@ export default templateCompositeFrom({
       '#mappedList': '#artistTagFilter',
     }),
 
-    withFilteredList({
-      list: '#contentNodes',
-      filter: '#artistTagFilter',
+    withFilteredList('#contentNodes', '#artistTagFilter')
+      .outputs({'#filteredList': '#artistTags'}),
+
+    withMappedList({
+      list: '#artistTags',
+      map: input('filterArtistTags'),
     }).outputs({
-      '#filteredList': '#artistTags',
+      '#mappedList': '#customFilter',
     }),
 
+    withFilteredList({list: '#artistTags', filter: '#customFilter'})
+      .outputs({'#filteredList': '#artistTags'}),
+
     withMappedList({
       list: '#artistTags',
       map: input.value(node =>
diff --git a/src/data/things/content/ContentEntry.js b/src/data/things/content/ContentEntry.js
index 04df303f..47f86622 100644
--- a/src/data/things/content/ContentEntry.js
+++ b/src/data/things/content/ContentEntry.js
@@ -1,5 +1,5 @@
 import {input, V} from '#composite';
-import {transposeArrays} from '#sugar';
+import {transposeArrays, unique} from '#sugar';
 import Thing from '#thing';
 import {is, isDate, validateReferenceList} from '#validators';
 import {parseDate} from '#yaml';
@@ -33,14 +33,43 @@ export class ContentEntry extends Thing {
 
     thing: thing(),
 
-    artists: [
+    headingArtists: [
       withExpressedOrImplicitArtistReferences({
-        from: input.updateValue({
+        fromExpressed: input.updateValue({
           validate: validateReferenceList('artist'),
         }),
+
+        fromContent: 'headingArtistText',
+      }),
+
+      withResolvedReferenceList({
+        list: '#artistReferences',
+        find: soupyFind.input('artist'),
       }),
 
-      exitWithoutDependency('#artistReferences', V([])),
+      exposeDependency('#resolvedReferenceList'),
+    ],
+
+    quotedArtists: [
+      exitWithoutDependency('body', V([])),
+
+      {
+        dependencies: ['body'],
+        compute: (continuation, {body}) => continuation({
+          ['#filterArtistTags']: node =>
+            /(\n|^)> <i>$/.test(body.slice(0, node.i)) &&
+            /^:<\/i>/.test(body.slice(node.iEnd)),
+        }),
+      },
+
+      withExpressedOrImplicitArtistReferences({
+        fromExpressed: input.updateValue({
+          validate: validateReferenceList('artist'),
+        }),
+
+        fromContent: 'body',
+        filterArtistTags: '#filterArtistTags',
+      }),
 
       withResolvedReferenceList({
         list: '#artistReferences',
@@ -50,7 +79,7 @@ export class ContentEntry extends Thing {
       exposeDependency('#resolvedReferenceList'),
     ],
 
-    artistText: contentString(),
+    headingArtistText: contentString(),
 
     annotation: contentString(),
 
@@ -119,6 +148,14 @@ export class ContentEntry extends Thing {
 
     isContentEntry: exposeConstant(V(true)),
 
+    artists: [
+      {
+        dependencies: ['headingArtists', 'quotedArtists'],
+        compute: ({headingArtists, quotedArtists}) =>
+          unique([...headingArtists, ...quotedArtists]),
+      },
+    ],
+
     annotationParts: [
       withAnnotationPartNodeLists(),
 
@@ -230,8 +267,10 @@ export class ContentEntry extends Thing {
 
   static [Thing.yamlDocumentSpec] = {
     fields: {
-      'Artists': {property: 'artists'},
-      'Artist Text': {property: 'artistText'},
+      'Artists': {property: 'headingArtists'},
+      'Artist Text': {property: 'headingArtistText'},
+
+      'Quoted Artists': {property: 'quotedArtists'},
 
       'Annotation': {property: 'annotation'},