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