« get me outta code hell

hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/content-function.js4
-rw-r--r--src/content/dependencies/index.js89
2 files changed, 82 insertions, 11 deletions
diff --git a/src/content-function.js b/src/content-function.js
index dbac691b..0a217800 100644
--- a/src/content-function.js
+++ b/src/content-function.js
@@ -383,6 +383,7 @@ export function quickEvaluate({
   // provided as part of allContentDependencies or allExtraDependencies.
   // Catch and report these early, together in an aggregate error.
   const unfulfilledErrors = [];
+  const unfulfilledNames = [];
   for (const name of neededContentDependencyNames) {
     const contentFunction = fulfilledContentDependencies[name];
     if (!contentFunction) continue;
@@ -392,12 +393,13 @@ export function quickEvaluate({
       } catch (error) {
         error.message = `(${name}) ${error.message}`;
         unfulfilledErrors.push(error);
+        unfulfilledNames.push(name);
       }
     }
   }
 
   if (!empty(unfulfilledErrors)) {
-    throw new AggregateError(unfulfilledErrors, `Content functions unfulfilled`);
+    throw new AggregateError(unfulfilledErrors, `Content functions unfulfilled (${unfulfilledNames.join(', ')})`);
   }
 
   const slotResults = {};
diff --git a/src/content/dependencies/index.js b/src/content/dependencies/index.js
index 5cd116d4..7f86abb1 100644
--- a/src/content/dependencies/index.js
+++ b/src/content/dependencies/index.js
@@ -7,12 +7,19 @@ import contentFunction from '../../content-function.js';
 import {color, logWarn} from '../../util/cli.js';
 import {annotateFunction} from '../../util/sugar.js';
 
-export function watchContentDependencies() {
+export function watchContentDependencies({
+  logging = true,
+} = {}) {
   const events = new EventEmitter();
   const contentDependencies = {};
 
+  let emittedReady = false;
+  let initialScanComplete = false;
+  let allDependenciesFulfilled = false;
+
   Object.assign(events, {
     contentDependencies,
+    close,
   });
 
   // Watch adjacent files
@@ -32,10 +39,42 @@ export function watchContentDependencies() {
       return;
     }
     handlePathRemoved(filePath);
-  })
+  });
+
+  watcher.on('ready', () => {
+    initialScanComplete = true;
+    checkReadyConditions();
+  });
 
   return events;
 
+  async function close() {
+    return watcher.close();
+  }
+
+  function checkReadyConditions() {
+    if (emittedReady) {
+      return;
+    }
+
+    if (!initialScanComplete) {
+      return;
+    }
+
+    checkAllDependenciesFulfilled();
+
+    if (!allDependenciesFulfilled) {
+      return;
+    }
+
+    events.emit('ready');
+    emittedReady = true;
+  }
+
+  function checkAllDependenciesFulfilled() {
+    allDependenciesFulfilled = !Object.values(contentDependencies).includes(null);
+  }
+
   function getFunctionName(filePath) {
     const shortPath = path.basename(filePath);
     const functionName = shortPath.slice(0, -path.extname(shortPath).length);
@@ -85,24 +124,54 @@ export function watchContentDependencies() {
       }
 
       contentDependencies[functionName] = fn;
+
+      events.emit('update', functionName);
+      checkReadyConditions();
     }
 
     if (!error) {
       return true;
     }
 
-    if (contentDependencies[functionName]) {
-      logWarn`Failed to import ${functionName} - using existing version`;
-    } else {
-      logWarn`Failed to import ${functionName} - no prior version loaded`;
+    if (!(functionName in contentDependencies)) {
+      contentDependencies[functionName] = null;
     }
 
-    if (typeof error === 'string') {
-      console.error(color.yellow(error));
-    } else {
-      console.error(error);
+    events.emit('error', functionName, error);
+
+    if (logging) {
+      if (contentDependencies[functionName]) {
+        logWarn`Failed to import ${functionName} - using existing version`;
+      } else {
+        logWarn`Failed to import ${functionName} - no prior version loaded`;
+      }
+
+      if (typeof error === 'string') {
+        console.error(color.yellow(error));
+      } else {
+        console.error(error);
+      }
     }
 
     return false;
   }
 }
+
+export function quickLoadContentDependencies() {
+  return new Promise((resolve, reject) => {
+    const watcher = watchContentDependencies();
+
+    watcher.on('error', (name, error) => {
+      watcher.close().then(() => {
+        error.message = `Error loading dependency ${name}: ${error}`;
+        reject(error);
+      });
+    });
+
+    watcher.on('ready', () => {
+      watcher.close().then(() => {
+        resolve(watcher.contentDependencies);
+      });
+    });
+  });
+}