Initial commit
commit
19aaa47cbb
@ -0,0 +1 @@
|
||||
node_modules
|
@ -0,0 +1,22 @@
|
||||
"use strict";
|
||||
|
||||
const Promise = require("bluebird");
|
||||
|
||||
const pipe = require("@promistream/pipe");
|
||||
const walkFolder = require(".");
|
||||
const map = require("@promistream/map");
|
||||
const filter = require("@promistream/filter");
|
||||
const find = require("@promistream/find");
|
||||
|
||||
return Promise.try(() => {
|
||||
return pipe([
|
||||
walkFolder(".", {
|
||||
// NOTE: Make sure that your predicate always includes the initial path, or nothing will happen
|
||||
throughStream: filter((path) => path === "." || path.includes("node_modules"))
|
||||
}),
|
||||
map((item) => item.path),
|
||||
find((path) => path.includes("bluebird"))
|
||||
]).read();
|
||||
}).then((result) => {
|
||||
console.log(result);
|
||||
});
|
@ -0,0 +1,88 @@
|
||||
/* eslint-disable no-undef */
|
||||
"use strict";
|
||||
|
||||
const Promise = require("bluebird");
|
||||
const fs = require("fs").promises;
|
||||
const path = require("path");
|
||||
const unreachable = require("@joepie91/unreachable")("@promistream/walk-folder");
|
||||
|
||||
const pipe = require("@promistream/pipe");
|
||||
const simpleQueue = require("@promistream/simple-queue");
|
||||
const parallelize = require("@promistream/parallelize");
|
||||
const mapFilter = require("@promistream/map-filter");
|
||||
|
||||
const { validateArguments } = require("@validatem/core");
|
||||
const required = require("@validatem/required");
|
||||
const isBoolean = require("@validatem/is-boolean");
|
||||
const isString = require("@validatem/is-string");
|
||||
const defaultTo = require("@validatem/default-to");
|
||||
|
||||
// TODO: Change API to also provide access to the corresponding stat object, not just the path?
|
||||
|
||||
module.exports = function createWalkFolderStream(_rootPath, _options) {
|
||||
let [ rootPath, options ] = validateArguments(arguments, {
|
||||
rootPath: [ required, isString ],
|
||||
options: [ defaultTo({}), {
|
||||
linear: [ defaultTo(false), isBoolean ],
|
||||
followSymlinks: [ defaultTo(true), isBoolean ],
|
||||
throughStream: [ defaultTo(null) ] // FIXME: either null or isPromistream, take implementation from `pipe`
|
||||
}]
|
||||
});
|
||||
|
||||
// linear: Whether to walk the tree linearly (breadth-first) instead of randomly. This will make output deterministic, usually at the cost of being slower. On some filesystems that deal poorly with concurrent I/O, this may actually be a little faster.
|
||||
// linear -> sort + no parallelize, random -> parallelize infinity unordered
|
||||
let queue = simpleQueue([ rootPath ]);
|
||||
let seenInodes = new Set(); // NOTE: Used for detecting and avoiding infinite loops through eg. symlinks
|
||||
|
||||
function queueDirectoryContents(targetPath) {
|
||||
return Promise.try(() => {
|
||||
return fs.readdir(targetPath);
|
||||
}).then((entries) => {
|
||||
if (options.linear === true) {
|
||||
entries.sort(); // To make the output deterministic
|
||||
}
|
||||
|
||||
return entries;
|
||||
}).map((item) => {
|
||||
return queue.push(path.join(targetPath, item));
|
||||
});
|
||||
}
|
||||
|
||||
let statStream = mapFilter((targetPath) => {
|
||||
return Promise.try(() => {
|
||||
if (options.followSymlinks === true) {
|
||||
return fs.stat(targetPath);
|
||||
} else {
|
||||
return fs.lstat(targetPath);
|
||||
}
|
||||
}).then((stats) => {
|
||||
if (stats.ino == null) {
|
||||
throw new unreachable("Stats without inode encountered");
|
||||
} else if (!seenInodes.has(stats.ino)) {
|
||||
seenInodes.add(stats.ino);
|
||||
|
||||
return Promise.try(() => {
|
||||
if (stats.isDirectory()) {
|
||||
return queueDirectoryContents(targetPath);
|
||||
}
|
||||
}).then(() => {
|
||||
return { path: targetPath, stats: stats };
|
||||
});
|
||||
} else {
|
||||
return mapFilter.NoValue;
|
||||
}
|
||||
}).catch((error) => {
|
||||
console.error(targetPath, error);
|
||||
});
|
||||
});
|
||||
|
||||
return pipe([
|
||||
queue.stream,
|
||||
options.throughStream,
|
||||
statStream,
|
||||
(options.linear !== false)
|
||||
? parallelize(Infinity, { ordered: false })
|
||||
: null
|
||||
]);
|
||||
};
|
||||
|
@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "@promistream/walk-folder",
|
||||
"version": "0.1.0",
|
||||
"main": "index.js",
|
||||
"repository": "http://git.cryto.net/promistream/walk-folder.git",
|
||||
"author": "Sven Slootweg <admin@cryto.net>",
|
||||
"license": "WTFPL OR CC0-1.0",
|
||||
"dependencies": {
|
||||
"@joepie91/unreachable": "^1.0.0",
|
||||
"@promistream/map-filter": "^0.1.0",
|
||||
"@promistream/parallelize": "^0.1.1",
|
||||
"@promistream/pipe": "^0.1.6",
|
||||
"@promistream/simple-queue": "^0.1.0",
|
||||
"@validatem/core": "^0.3.15",
|
||||
"@validatem/default-to": "^0.1.0",
|
||||
"@validatem/is-boolean": "^0.1.1",
|
||||
"@validatem/is-string": "^1.0.0",
|
||||
"@validatem/required": "^0.1.1",
|
||||
"bluebird": "^3.7.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@joepie91/eslint-config": "^1.1.0",
|
||||
"@promistream/filter": "^0.1.1",
|
||||
"@promistream/find": "^0.1.0",
|
||||
"@promistream/map": "^0.1.1",
|
||||
"eslint": "^7.30.0"
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue