add some options
This commit is contained in:
parent
7001b3e615
commit
67df50bb1b
15
README.md
15
README.md
|
@ -2,6 +2,9 @@
|
|||
|
||||
Walk a directory recursively and handle each entity (files, directories, symlnks, etc).
|
||||
|
||||
(a port of Go's [`filepath.Walk`](https://golang.org/pkg/path/filepath/#Walk)
|
||||
using Node.js v10+'s `fs.readdir`'s `withFileTypes` and ES 2021)
|
||||
|
||||
```js
|
||||
await Walk.walk(pathname, walkFunc);
|
||||
|
||||
|
@ -10,8 +13,16 @@ function walkFunc(err, pathname, dirent) {
|
|||
}
|
||||
```
|
||||
|
||||
This is a port of Go's [`filepath.Walk`](https://golang.org/pkg/path/filepath/#Walk)
|
||||
for Node.js v10+ (which introduced `fs.readdir` `withFileTypes`) and ES 2021.
|
||||
Where
|
||||
|
||||
- `err` is a failure to lstat a file or directory
|
||||
- `pathname` may be relative
|
||||
- `dirent` is an `fs.Dirent` that has
|
||||
- `dirent.name`
|
||||
- `dirent.isFile()`
|
||||
- `dirent.isDirectory()`
|
||||
- `dirent.isSymbolicLink()`
|
||||
- etc
|
||||
|
||||
# Examples
|
||||
|
||||
|
|
15
bin/walk.js
15
bin/walk.js
|
@ -1,16 +1,27 @@
|
|||
import Walk from "../index.js";
|
||||
import path from "path";
|
||||
//import { walk } from "../index.js";
|
||||
|
||||
import Walk from "../index.js";
|
||||
var walk = Walk.create({
|
||||
sort: function (ents) {
|
||||
return ents.filter(function (ent) {
|
||||
return !ent.name.startsWith(".");
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
var rootpath = process.argv[2] || ".";
|
||||
|
||||
Walk.walk(rootpath, async function (err, pathname, dirent) {
|
||||
walk(rootpath, async function (err, pathname, dirent) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
/*
|
||||
if (dirent.name.startsWith(".")) {
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
|
||||
var entType;
|
||||
if (dirent.isDirectory()) {
|
||||
|
|
120
create.js
120
create.js
|
@ -1,10 +1,9 @@
|
|||
// a port of Go's filepath.Walk
|
||||
|
||||
import { promises as fs } from "fs";
|
||||
import { skipDir } from "./walk.js";
|
||||
import Walk from "./walk.js";
|
||||
import path from "path";
|
||||
|
||||
var Walk = {};
|
||||
var _withFileTypes = { withFileTypes: true };
|
||||
var _noopts = {};
|
||||
|
||||
|
@ -12,97 +11,64 @@ function pass(err) {
|
|||
return err;
|
||||
}
|
||||
|
||||
function skipOrThrow(err) {
|
||||
if (!(err instanceof Error)) {
|
||||
// go
|
||||
return false;
|
||||
}
|
||||
if (false === err || skipDir === err) {
|
||||
// skip
|
||||
return true;
|
||||
}
|
||||
// throw
|
||||
throw err;
|
||||
}
|
||||
|
||||
Walk.skipDir = skipDir;
|
||||
|
||||
Walk.create = function (opts) {
|
||||
async function _walk(root, walker) {
|
||||
if (!opts) {
|
||||
opts = _noopts;
|
||||
}
|
||||
|
||||
// Special case of the very first item, root
|
||||
var err;
|
||||
var stat = await fs.lstat(root).catch(function (e) {
|
||||
err = e;
|
||||
return null;
|
||||
});
|
||||
if (stat) {
|
||||
stat.name = path.basename(path.resolve(root));
|
||||
// a port of Go's filepath.Walk
|
||||
const _walk = async (pathname, walkFunc, _dirent) => {
|
||||
let err;
|
||||
|
||||
// special case of the very first run
|
||||
if (!_dirent) {
|
||||
_dirent = pathname;
|
||||
}
|
||||
|
||||
/* similar to function in main walk loop */
|
||||
var uerr = await walker(err, root, stat).then(pass).catch(pass);
|
||||
if (skipOrThrow(uerr) || err) {
|
||||
// the first run, or if false === withFileTypes
|
||||
if ("string" === typeof _dirent) {
|
||||
let _name = path.basename(path.resolve(pathname));
|
||||
_dirent = await fs.lstat(pathname).catch(pass);
|
||||
if (_dirent instanceof Error) {
|
||||
err = _dirent;
|
||||
} else {
|
||||
_dirent.name = _name;
|
||||
}
|
||||
}
|
||||
|
||||
// run the user-supplied function and either skip, bail, or continue
|
||||
err = await walkFunc(err, pathname, _dirent).catch(pass);
|
||||
if (false === err || Walk.skipDir === err) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (false === opts.withFileTypes) {
|
||||
stat = stat.name;
|
||||
}
|
||||
if (opts.sort) {
|
||||
stat = (opts.sort([stat]) || [])[0];
|
||||
}
|
||||
|
||||
if (stat && stat.isDirectory()) {
|
||||
return _walkHelper(root, stat, walker, opts);
|
||||
}
|
||||
/* end */
|
||||
|
||||
async function _walkHelper(root, prevEnt, walker, opts) {
|
||||
var err;
|
||||
var _readdirOpts;
|
||||
if (false !== opts.withFileTypes) {
|
||||
_readdirOpts = _withFileTypes;
|
||||
}
|
||||
var dirents = await fs.readdir(root, _readdirOpts).catch(function (e) {
|
||||
err = e;
|
||||
});
|
||||
if (err) {
|
||||
return walker(err, root, prevEnt);
|
||||
}
|
||||
if (opts.sort) {
|
||||
dirents = opts.sort(dirents);
|
||||
}
|
||||
|
||||
var dirent;
|
||||
var pathname;
|
||||
for (dirent of dirents) {
|
||||
if ("string" === typeof dirent) {
|
||||
pathname = path.join(root, dirent);
|
||||
dirent = await fs.lstat(path.join(root)).catch(function (e) {
|
||||
err = e;
|
||||
});
|
||||
} else {
|
||||
pathname = path.join(root, dirent.name);
|
||||
}
|
||||
|
||||
/* main inner loop */
|
||||
err = await walker(err, pathname, dirent).then(pass).catch(pass);
|
||||
if (skipOrThrow(err)) {
|
||||
continue;
|
||||
if (err instanceof Error) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
// "walk does not follow symbolic links"
|
||||
if (dirent.isDirectory()) {
|
||||
await _walkHelper(path.join(root, dirent.name), dirent, walker, opts);
|
||||
if (!_dirent || !_dirent.isDirectory()) {
|
||||
return;
|
||||
}
|
||||
/* end */
|
||||
|
||||
// lightweight dirents or full lstat
|
||||
let _readdirOpts;
|
||||
if (!opts.withFileStats) {
|
||||
_readdirOpts = _withFileTypes;
|
||||
}
|
||||
|
||||
// TODO check if the error is "not a directory"
|
||||
// (and thus allow false === opts.withFileTypes)
|
||||
let result = await fs.readdir(pathname, _readdirOpts).catch(pass);
|
||||
if (result instanceof Error) {
|
||||
return walkFunc(result, pathname, _dirent);
|
||||
}
|
||||
if (opts.sort) {
|
||||
result = opts.sort(result);
|
||||
}
|
||||
for (let entity of result) {
|
||||
await _walk(path.join(pathname, entity.name || entity), walkFunc, entity);
|
||||
}
|
||||
};
|
||||
|
||||
return _walk;
|
||||
};
|
||||
|
|
3
index.js
3
index.js
|
@ -1,3 +1,6 @@
|
|||
import Walk from "./walk.js";
|
||||
import Walk2 from "./create.js";
|
||||
|
||||
Walk.create = Walk2.create;
|
||||
|
||||
export default Walk;
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
"main": "index.js",
|
||||
"type": "module",
|
||||
"files": [
|
||||
"walk.js"
|
||||
"walk.js",
|
||||
"create.js"
|
||||
],
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
// ECMAScript 2021
|
||||
// (or Vanilla JS)
|
||||
import { promises as fs } from "fs";
|
||||
// or let fs = require("fs").promises;
|
||||
import path from "path";
|
||||
// or let path = require("path");
|
||||
|
||||
// a port of Go's filepath.Walk
|
||||
async function walk(pathname, walkFunc, _dirent) {
|
||||
let err;
|
||||
function pass(e) {
|
||||
return e;
|
||||
}
|
||||
|
||||
// special case of the very first run
|
||||
if (!_dirent) {
|
||||
let _name = path.basename(path.resolve(pathname));
|
||||
_dirent = await fs.lstat(pathname).catch(pass);
|
||||
if (_dirent instanceof Error) {
|
||||
err = _dirent;
|
||||
} else {
|
||||
_dirent.name = _name;
|
||||
}
|
||||
}
|
||||
|
||||
// run the user-supplied function and either skip, bail, or continue
|
||||
err = await walkFunc(err, pathname, _dirent).catch(pass);
|
||||
if (false === err) {
|
||||
return;
|
||||
}
|
||||
if (err instanceof Error) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
// "walk does not follow symbolic links"
|
||||
if (!_dirent.isDirectory()) {
|
||||
return;
|
||||
}
|
||||
let result = await fs.readdir(pathname, { withFileTypes: true }).catch(pass);
|
||||
if (result instanceof Error) {
|
||||
return walkFunc(result, pathname, _dirent);
|
||||
}
|
||||
for (let dirent of result) {
|
||||
await walk(path.join(pathname, dirent.name), walkFunc, dirent);
|
||||
}
|
||||
}
|
||||
|
||||
walk("./", function (err, pathname, dirent) {
|
||||
if (dirent.name.startsWith(".")) {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
console.log(path.resolve(pathname));
|
||||
return Promise.resolve(true);
|
||||
});
|
14
walk.js
14
walk.js
|
@ -6,13 +6,13 @@ const skipDir = new Error("skip this directory");
|
|||
const pass = (err) => err;
|
||||
|
||||
// a port of Go's filepath.Walk
|
||||
const walk = async (root, walkFunc, _dirent) => {
|
||||
const walk = async (pathname, walkFunc, _dirent) => {
|
||||
let err;
|
||||
|
||||
// special case of the very first run
|
||||
if (!_dirent) {
|
||||
let _name = path.basename(path.resolve(root));
|
||||
_dirent = await fs.lstat(root).catch(pass);
|
||||
let _name = path.basename(path.resolve(pathname));
|
||||
_dirent = await fs.lstat(pathname).catch(pass);
|
||||
if (_dirent instanceof Error) {
|
||||
err = _dirent;
|
||||
} else {
|
||||
|
@ -21,7 +21,7 @@ const walk = async (root, walkFunc, _dirent) => {
|
|||
}
|
||||
|
||||
// run the user-supplied function and either skip, bail, or continue
|
||||
err = await walkFunc(err, root, _dirent).catch(pass);
|
||||
err = await walkFunc(err, pathname, _dirent).catch(pass);
|
||||
if (false === err || skipDir === err) {
|
||||
return;
|
||||
}
|
||||
|
@ -33,12 +33,12 @@ const walk = async (root, walkFunc, _dirent) => {
|
|||
if (!_dirent.isDirectory()) {
|
||||
return;
|
||||
}
|
||||
let result = await fs.readdir(root, _withFileTypes).catch(pass);
|
||||
let result = await fs.readdir(pathname, _withFileTypes).catch(pass);
|
||||
if (result instanceof Error) {
|
||||
return walkFunc(result, root, _dirent);
|
||||
return walkFunc(result, pathname, _dirent);
|
||||
}
|
||||
for (let dirent of result) {
|
||||
await walk(path.join(root, dirent.name), walkFunc, dirent);
|
||||
await walk(path.join(pathname, dirent.name), walkFunc, dirent);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue