UNPKG

filepaths-group

Version:

group arrays of file objects that have a .path property

112 lines (108 loc) 3.66 kB
var path = require('path') var count = require('count-top-entries') var wishlist = require('f-d-wishlist') function gotAllNestedFiles (dir, map, cb) { // cb tells the truth count(dir, function (err, data) { if (err) return cb(err) if (!map[dir] || (data.files !== map[dir].length)) { cb(null, false) } else if (!data.dirs) { cb(null, true) } else { wishlist(dir, { full: true }, function (err, wishlist) { if (err) return cb(err) wishlist.dirs.forEach(function (d) { gotAllNestedFiles(d, map, cb) }) }) } }) } // designed to be used with ferross drag-drop module function group (files, opts, callback) { // opts: { size: boolean } if (typeof opts === 'function') return group(files, {}, opts) var allAbs = files.every(function (file) { return path.isAbsolute(file.path) }) if (!allAbs) return callback(Error('cannot yet handle relative filepaths')) var trap = { dirs: [], files: [], paths: [], fdir: [], map: {}, temp: [] } var groups = [] Array.prototype.push.apply(trap.paths, files.map(function (file) { return file.path })) // if single file input always early return as single file if (!trap.paths.length) { return callback(null, []) } else if (trap.paths.length === 1) { var singleton = { type: 'file', path: trap.paths[0] } if (opts.size) singleton.size = files[0].size return callback(null, [ singleton ]) } // split paths into file objects trap.fdir = trap.paths.map(function (filepath) { return { path: filepath, dir: path.dirname(filepath) } }) // map files to dirs trap.map = trap.fdir.reduce(function (acc, cur) { if (acc.hasOwnProperty(cur.dir)) acc[cur.dir].push(cur.path) else acc[cur.dir] = [ cur.path ] return acc }, {}) // push keys of props that represent an entire dir to trap.dirs... via trap.t var dirs = Object.keys(trap.map) var pending = dirs.length dirs.forEach(function (dir) { gotAllNestedFiles(dir, trap.map, function (err, truth) { if (err) return callback(err, null) if (truth) trap.temp.push(dir) if (!--pending) finishUp() }) }) // finish function finishUp () { // push paths that are not covered by trap.temp to trap.files Array.prototype.push.apply(trap.files, trap.fdir.filter(function (file) { return !trap.temp.some(function (dir) { return dir === file.dir }) }).map(function (file) { return file.path }) ) // collapse nested dirs in trap.temp to trap.dirs Array.prototype.push.apply(trap.dirs, trap.temp.filter(function (dir, i, arr) { return !arr.filter(function (d) { return d !== dir }).some(function (other) { return dir.startsWith(other) }) }) ) // package neatly groups = trap.files.map(function (file) { return { type: 'file', path: file } }).concat(trap.dirs.map(function (dir) { return { type: 'directory', path: dir } })) // maybe add size if (opts.size) { groups = groups.map(function (item, i) { if (item.type === 'file') { item.size = files.find(function (file) { return file.path === item.path }).size } else { item.size = files.reduce(function (acc, cur) { if (cur.path.startsWith(item.path)) acc += cur.size return acc }, 0) } return item }) } callback(null, groups) } } module.exports = group