« 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/content-function.js16
-rw-r--r--src/util/sugar.js76
2 files changed, 86 insertions, 6 deletions
diff --git a/src/content-function.js b/src/content-function.js
index 51383793..654a294c 100644
--- a/src/content-function.js
+++ b/src/content-function.js
@@ -1,4 +1,4 @@
-import {empty} from './util/sugar.js';
+import {annotateFunction, empty} from './util/sugar.js';
 
 export default function contentFunction({
   contentDependencies = [],
@@ -54,6 +54,7 @@ export function expectDependencies({
       throw new Error(`Generate invalidated because unfulfilled dependencies provided: ${invalidatingDependencyKeys.join(', ')}`);
     };
 
+    annotateFunction(wrappedGenerate, {name: generate, trait: 'invalidated'});
     wrappedGenerate.fulfilled ??= false;
   }
 
@@ -62,17 +63,19 @@ export function expectDependencies({
       return generate(data, fulfilledDependencies);
     };
 
+    annotateFunction(wrappedGenerate, {name: generate, trait: 'fulfilled'});
+    wrappedGenerate.fulfilled ??= true;
+
     wrappedGenerate.fulfill = function() {
       throw new Error(`All dependencies already fulfilled`);
     };
-
-    wrappedGenerate.fulfilled ??= true;
   }
 
   wrappedGenerate ??= function() {
     throw new Error(`Dependencies still needed: ${missingContentDependencyKeys.concat(missingExtraDependencyKeys).join(', ')}`);
   };
 
+  annotateFunction(wrappedGenerate, {name: generate, trait: 'unfulfilled'});
   wrappedGenerate.fulfilled ??= false;
   wrappedGenerate[contentFunction.identifyingSymbol] = true;
 
@@ -84,6 +87,7 @@ export function expectDependencies({
         throw new Error(`Expected call to this dependency's .data()`);
       };
 
+      annotateFunction(wrappedGenerate, {name: fulfilledDependencies[key], description: 'data only'});
       wrappedDependency.data = fulfilledDependencies[key].data;
       dataDependencies[key] = wrappedDependency;
     }
@@ -91,13 +95,17 @@ export function expectDependencies({
     wrappedGenerate.data = function(...args) {
       return data(...args, dataDependencies);
     };
+
+    annotateFunction(wrappedGenerate.data, {name: data, trait: 'fulfilled'});
   }
 
   wrappedGenerate.data ??= function() {
     throw new Error(`Dependencies still needed: ${missingContentDependencyKeys.join(', ')}`);
   };
 
-  wrappedGenerate.fulfill ??= function(dependencies) {
+  annotateFunction(wrappedGenerate.data, {name: data, trait: 'unfulfilled'});
+
+  wrappedGenerate.fulfill ??= function fulfill(dependencies) {
     return expectDependencies({
       generate,
       data,
diff --git a/src/util/sugar.js b/src/util/sugar.js
index c60bddb6..ad36d16b 100644
--- a/src/util/sugar.js
+++ b/src/util/sugar.js
@@ -146,8 +146,9 @@ export function bindOpts(fn, bind) {
     ]);
   };
 
-  Object.defineProperty(bound, 'name', {
-    value: fn.name ? `(options-bound) ${fn.name}` : `(options-bound)`,
+  annotateFunction(bound, {
+    name: fn,
+    trait: 'options-bound',
   });
 
   for (const [key, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(fn))) {
@@ -487,3 +488,74 @@ export function decorateErrorWithIndex(fn) {
     }
   };
 }
+
+// Delicious function annotations, such as:
+//
+//   (*bound) soWeAreBackInTheMine
+//   (data *unfulfilled) generateShrekTwo
+//
+export function annotateFunction(fn, {
+  name: nameOrFunction = null,
+  description: newDescription,
+  trait: newTrait,
+}) {
+  let name;
+
+  if (typeof nameOrFunction === 'function') {
+    name = nameOrFunction.name;
+  } else if (typeof nameOrFunction === 'string') {
+    name = nameOrFunction;
+  }
+
+  name ??= fn.name ?? 'anonymous';
+
+  const match = name.match(/^ *(?<prefix>.*?) *\((?<description>.*)( #(?<trait>.*))?\) *(?<suffix>.*) *$/);
+
+  let prefix, suffix, description, trait;
+  if (match) {
+    ({prefix, suffix, description, trait} = match.groups);
+  }
+
+  prefix ??= '';
+  suffix ??= name;
+  description ??= '';
+  trait ??= '';
+
+  if (newDescription) {
+    if (description) {
+      description += '; ' + newDescription;
+    } else {
+      description = newDescription;
+    }
+  }
+
+  if (newTrait) {
+    if (trait) {
+      trait += ' #' + newTrait;
+    } else {
+      trait = '#' + newTrait;
+    }
+  }
+
+  let parenthesesPart;
+
+  if (description && trait) {
+    parenthesesPart = `${description} ${trait}`;
+  } else if (description || trait) {
+    parenthesesPart = description || trait;
+  } else {
+    parenthesesPart = '';
+  }
+
+  let finalName;
+
+  if (prefix && parenthesesPart) {
+    finalName = `${prefix} (${parenthesesPart}) ${suffix}`;
+  } else if (parenthesesPart) {
+    finalName = `(${parenthesesPart}) ${suffix}`;
+  } else {
+    finalName = suffix;
+  }
+
+  Object.defineProperty(fn, 'name', {value: finalName});
+}