« get me outta code hell

hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
path: root/src/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/util')
-rw-r--r--src/util/io.js23
-rw-r--r--src/util/node-utils.js51
2 files changed, 32 insertions, 42 deletions
diff --git a/src/util/io.js b/src/util/io.js
deleted file mode 100644
index 12e87f4d..00000000
--- a/src/util/io.js
+++ /dev/null
@@ -1,23 +0,0 @@
-// Utility functions for interacting with files and other external data
-// interfacey constructs.
-
-import {readdir} from 'fs/promises';
-import * as path from 'path';
-
-export async function findFiles(dataPath, {
-  filter = () => true,
-  joinParentDirectory = true,
-} = {}) {
-  let files;
-  try {
-    files = await readdir(dataPath);
-  } catch (error) {
-    throw Object.assign(
-      new AggregateError([error], `Failed to list files from ${dataPath}`),
-      {code: error.code});
-  }
-
-  return files
-    .filter((file) => filter(file))
-    .map((file) => (joinParentDirectory ? path.join(dataPath, file) : file));
-}
diff --git a/src/util/node-utils.js b/src/util/node-utils.js
index 3c0dd4cd..2fb7e8dd 100644
--- a/src/util/node-utils.js
+++ b/src/util/node-utils.js
@@ -1,6 +1,6 @@
 // Utility functions which are only relevant to particular Node.js constructs.
 
-import {readdir} from 'fs/promises';
+import {readdir, stat} from 'fs/promises';
 import {fileURLToPath} from 'url';
 import * as path from 'path';
 
@@ -56,34 +56,47 @@ export function isMain(importMetaURL) {
   ].includes(relative);
 }
 
-// Like readdir... but it's recursive!
-export function traverse(startDirPath, {
+// Like readdir... but it's recursive! This returns a flat list of file paths.
+// By default, the paths include the provided top/root path, but this can be
+// changed with prefixPath to prefix some other path, or to just return paths
+// relative to the root. Change pathStyle to specify posix or win32, or leave
+// it as the default device-correct style. Provide a filterDir function to
+// control which directory names are traversed at all, and filterFile to
+// select which filenames are included in the final list.
+export async function traverse(rootPath, {
   pathStyle = 'device',
   filterFile = () => true,
-  filterDir = () => true
+  filterDir = () => true,
+  prefixPath = rootPath,
 } = {}) {
-  const pathJoin = {
+  const pathJoinDevice = path.join;
+  const pathJoinStyle = {
     'device': path.join,
     'posix': path.posix.join,
     'win32': path.win32.join,
   }[pathStyle];
 
-  if (!pathJoin) {
+  if (!pathJoinStyle) {
     throw new Error(`Expected pathStyle to be device, posix, or win32`);
   }
 
-  const recursive = (names, subDirPath) =>
-    Promise.all(names.map(name =>
-      readdir(pathJoin(startDirPath, subDirPath, name)).then(
-        names =>
-          (filterDir(name)
-            ? recursive(names, pathJoin(subDirPath, name))
-            : []),
-        () =>
-          (filterFile(name)
-            ? [pathJoin(subDirPath, name)]
-            : []))))
-      .then(pathArrays => pathArrays.flat());
+  const recursive = (names, ...subdirectories) =>
+    Promise.all(names.map(async name => {
+      const devicePath = pathJoinDevice(rootPath, ...subdirectories, name);
+      const stats = await stat(devicePath);
 
-  return readdir(startDirPath).then(names => recursive(names, ''));
+      if (stats.isDirectory() && !filterDir(name)) return [];
+      else if (stats.isFile() && !filterFile(name)) return [];
+      else if (!stats.isDirectory() && !stats.isFile()) return [];
+
+      if (stats.isDirectory()) {
+        return recursive(await readdir(devicePath), ...subdirectories, name);
+      } else {
+        return pathJoinStyle(prefixPath, ...subdirectories, name);
+      }
+    }));
+
+  const names = await readdir(rootPath);
+  const results = await recursive(names);
+  return results.flat(Infinity);
 }