mirror of
https://github.com/bcomnes/deploy-to-neocities.git
synced 2026-01-17 06:56:30 +00:00
180 lines
4.9 KiB
JavaScript
180 lines
4.9 KiB
JavaScript
const fs = require('fs')
|
|
const path = require('path')
|
|
|
|
const { readdir, lstat } = fs.promises
|
|
|
|
/**
|
|
* pathFilter lets you filter files based on a resolved `filepath`.
|
|
* @callback pathFilter
|
|
* @param {String} filepath - The resolved `filepath` of the file to test for filtering.
|
|
*
|
|
* @return {Boolean} Return false to filter the given `filepath` and true to include it.
|
|
*/
|
|
const pathFilter = filepath => true
|
|
|
|
/**
|
|
* statFilter lets you filter files based on a lstat object.
|
|
* @callback statFilter
|
|
* @param {Object} st - A fs.Stats instance.
|
|
*
|
|
* @return {Boolean} Return false to filter the given `filepath` and true to include it.
|
|
*/
|
|
const statFilter = filepath => true
|
|
|
|
/**
|
|
* FWStats is the object that the okdistribute/folder-walker module returns by default.
|
|
*
|
|
* @typedef FWStats
|
|
* @property {String} root - The filepath of the directory where the walk started.
|
|
* @property {String} filepath - The resolved filepath.
|
|
* @property {Object} stat - A fs.Stats instance.
|
|
* @property {String} relname - The relative path to `root`.
|
|
* @property {String} basename - The resolved filepath of the files containing directory.
|
|
*/
|
|
|
|
/**
|
|
* shaper lets you change the shape of the returned file data from walk-time stats.
|
|
* @callback shaper
|
|
* @param {FWStats} fwStats - The same status object returned from folder-walker.
|
|
*
|
|
* @return {*} - Whatever you want returned from the directory walk.
|
|
*/
|
|
const shaper = ({ root, filepath, stat, relname, basename }) => filepath
|
|
|
|
/**
|
|
* Options object
|
|
*
|
|
* @typedef Opts
|
|
* @property {pathFilter} [pathFilter] - A pathFilter cb.
|
|
* @property {statFilter} [statFilter] - A statFilter cb.
|
|
* @property {Number} [maxDepth=Infinity] - The maximum number of folders to walk down into.
|
|
* @property {shaper} [shaper] - A shaper cb.
|
|
*/
|
|
|
|
/**
|
|
* Create an async generator that iterates over all folders and directories inside of `dirs`.
|
|
*
|
|
* @async
|
|
* @generator
|
|
* @function
|
|
* @public
|
|
* @param {String|String[]} dirs - The path of the directory to walk, or an array of directory paths.
|
|
* @param {?(Opts)} opts - Options used for the directory walk.
|
|
*
|
|
* @yields {Promise<String|any>} - An async iterator that returns anything.
|
|
*/
|
|
async function * asyncFolderWalker (dirs, opts) {
|
|
opts = Object.assign({
|
|
fs,
|
|
pathFilter,
|
|
statFilter,
|
|
maxDepth: Infinity,
|
|
shaper
|
|
}, opts)
|
|
|
|
const roots = [dirs].flat().filter(opts.pathFilter)
|
|
const pending = []
|
|
|
|
while (roots.length) {
|
|
const root = roots.shift()
|
|
pending.push(root)
|
|
|
|
while (pending.length) {
|
|
const current = pending.shift()
|
|
if (typeof current === 'undefined') continue
|
|
const st = await lstat(current)
|
|
if ((!st.isDirectory() || depthLimiter(current, root, opts.maxDepth)) && opts.statFilter(st)) {
|
|
yield opts.shaper(fwShape(root, current, st))
|
|
continue
|
|
}
|
|
|
|
const files = await readdir(current)
|
|
files.sort()
|
|
|
|
for (const file of files) {
|
|
var next = path.join(current, file)
|
|
if (opts.pathFilter(next)) pending.unshift(next)
|
|
}
|
|
if (current === root || !opts.statFilter(st)) continue
|
|
else yield opts.shaper(fwShape(root, current, st))
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generates the same shape as the folder-walker module.
|
|
*
|
|
* @function
|
|
* @private
|
|
* @param {String} root - Root filepath.
|
|
* @param {String} name - Target filepath.
|
|
* @param {Object} st - fs.Stat object.
|
|
*
|
|
* @return {FWStats} - Folder walker object.
|
|
*/
|
|
function fwShape (root, name, st) {
|
|
return {
|
|
root: root,
|
|
filepath: name,
|
|
stat: st,
|
|
relname: root === name ? path.basename(name) : path.relative(root, name),
|
|
basename: path.basename(name)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test if we are at maximum directory depth.
|
|
*
|
|
* @private
|
|
* @function
|
|
* @param {String} filePath - The resolved path of the target fille.
|
|
* @param {String} relativeTo - The root directory of the current walk.
|
|
* @param {Number} maxDepth - The maximum number of folders to descend into.
|
|
*
|
|
* @returns {Boolean} - Return true to signal stop descending.
|
|
*/
|
|
function depthLimiter (filePath, relativeTo, maxDepth) {
|
|
if (maxDepth === Infinity) return false
|
|
const rootDepth = relativeTo.split(path.sep).length
|
|
const fileDepth = filePath.split(path.sep).length
|
|
return fileDepth - rootDepth > maxDepth
|
|
}
|
|
|
|
/**
|
|
* Async iterable collector
|
|
*
|
|
* @async
|
|
* @function
|
|
* @private
|
|
*/
|
|
async function all (iterator) {
|
|
const collect = []
|
|
|
|
for await (const result of iterator) {
|
|
collect.push(result)
|
|
}
|
|
|
|
return collect
|
|
}
|
|
|
|
/**
|
|
* allFiles gives you all files from the directory walk as an array.
|
|
*
|
|
* @async
|
|
* @function
|
|
* @public
|
|
* @param {String|String[]} dirs - The path of the directory to walk, or an array of directory paths.
|
|
* @param {?(Opts)} opts - Options used for the directory walk.
|
|
*
|
|
* @returns {Promise<String[]|any>} - An async iterator that returns anything.
|
|
*/
|
|
async function allFiles (...args) {
|
|
return all(asyncFolderWalker(...args))
|
|
}
|
|
|
|
module.exports = {
|
|
asyncFolderWalker,
|
|
allFiles,
|
|
all
|
|
}
|