diff options
Diffstat (limited to 'src/util/node-utils.js')
-rw-r--r-- | src/util/node-utils.js | 57 |
1 files changed, 55 insertions, 2 deletions
diff --git a/src/util/node-utils.js b/src/util/node-utils.js index 252e920..345d10a 100644 --- a/src/util/node-utils.js +++ b/src/util/node-utils.js @@ -1,6 +1,8 @@ // Utility functions which are only relevant to particular Node.js constructs. -import {fileURLToPath} from 'url'; +import {readdir, stat} from 'node:fs/promises'; +import * as path from 'node:path'; +import {fileURLToPath} from 'node:url'; import _commandExists from 'command-exists'; @@ -45,5 +47,56 @@ export function promisifyProcess(proc, showLogging = true) { // is great 'cuz (module === require.main) doesn't work without CommonJS // modules. export function isMain(importMetaURL) { - return process.argv[1] === fileURLToPath(importMetaURL); + const metaPath = fileURLToPath(importMetaURL); + const relative = path.relative(process.argv[1], metaPath); + const isIndexJS = path.basename(metaPath) === 'index.js'; + return [ + '', + isIndexJS && 'index.js' + ].includes(relative); +} + +// 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, + prefixPath = rootPath, +} = {}) { + const pathJoinDevice = path.join; + const pathJoinStyle = { + 'device': path.join, + 'posix': path.posix.join, + 'win32': path.win32.join, + }[pathStyle]; + + if (!pathJoinStyle) { + throw new Error(`Expected pathStyle to be device, posix, or win32`); + } + + const recursive = (names, ...subdirectories) => + Promise.all(names.map(async name => { + const devicePath = pathJoinDevice(rootPath, ...subdirectories, name); + const stats = await stat(devicePath); + + 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); } |