« 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/things/validators.js59
1 files changed, 49 insertions, 10 deletions
diff --git a/src/data/things/validators.js b/src/data/things/validators.js
index 2893e7fd..569a7b34 100644
--- a/src/data/things/validators.js
+++ b/src/data/things/validators.js
@@ -5,7 +5,8 @@ import printable_characters from 'printable-characters';
 const {strlen} = printable_characters;
 
 import {colors, ENABLE_COLOR} from '#cli';
-import {empty, typeAppearance, withAggregate} from '#sugar';
+import {cut, empty, typeAppearance, withAggregate} from '#sugar';
+import {commentaryRegex} from '#wiki-data';
 
 function inspect(value) {
   return nodeInspect(value, {colors: ENABLE_COLOR});
@@ -248,18 +249,56 @@ export function isColor(color) {
   throw new TypeError(`Unknown color format`);
 }
 
-export function isCommentary(commentary) {
-  isString(commentary);
+export function isCommentary(commentaryText) {
+  isString(commentaryText);
 
-  const [firstLine] = commentary.match(/.*/);
-  if (!firstLine.replace(/<\/b>/g, '').includes(':</i>')) {
-    throw new TypeError(`Missing commentary citation: "${
-      firstLine.length > 40
-        ? firstLine.slice(0, 40) + '...'
-        : firstLine
-    }"`);
+  const rawMatches =
+    Array.from(commentaryText.matchAll(commentaryRegex));
+
+  if (empty(rawMatches)) {
+    throw new TypeError(`Expected at least one commentary heading`);
   }
 
+  const niceMatches =
+    rawMatches.map(match => ({
+      position: match.index,
+      length: match[0].length,
+    }));
+
+  validateArrayItems(({position, length}, index) => {
+    if (index === 0 && position > 0) {
+      throw new TypeError(`Expected first commentary heading to be at top`);
+    }
+
+    const ownInput = commentaryText.slice(position, position + length);
+    const restOfInput = commentaryText.slice(position + length);
+    const nextLineBreak = restOfInput.indexOf('\n');
+    const upToNextLineBreak = restOfInput.slice(0, nextLineBreak);
+
+    if (/\S/.test(upToNextLineBreak)) {
+      throw new TypeError(
+        `Expected commentary heading to occupy entire line, got extra text:\n` +
+        `${colors.green(`"${cut(ownInput, 40)}"`)} (<- heading)\n` +
+        `(extra on same line ->) ${colors.red(`"${cut(upToNextLineBreak, 30)}"`)}\n` +
+        `(Check for missing "|-" in YAML, or a misshapen annotation)`);
+    }
+
+    const nextHeading =
+      (index === niceMatches.length - 1
+        ? commentaryText.length
+        : niceMatches[index + 1].position);
+
+    const upToNextHeading =
+      commentaryText.slice(position + length, nextHeading);
+
+    if (!/\S/.test(upToNextHeading)) {
+      throw new TypeError(
+        `Expected commentary entry to have body text, only got a heading`);
+    }
+
+    return true;
+  })(niceMatches);
+
   return true;
 }