add some options

This commit is contained in:
AJ ONeal 2020-12-09 04:48:55 -07:00
parent 7001b3e615
commit 67df50bb1b
7 changed files with 140 additions and 93 deletions

View File

@ -2,6 +2,9 @@
Walk a directory recursively and handle each entity (files, directories, symlnks, etc). 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 ```js
await Walk.walk(pathname, walkFunc); 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) Where
for Node.js v10+ (which introduced `fs.readdir` `withFileTypes`) and ES 2021.
- `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 # Examples

View File

@ -1,16 +1,27 @@
import Walk from "../index.js";
import path from "path"; 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] || "."; var rootpath = process.argv[2] || ".";
Walk.walk(rootpath, async function (err, pathname, dirent) { walk(rootpath, async function (err, pathname, dirent) {
if (err) { if (err) {
throw err; throw err;
} }
/*
if (dirent.name.startsWith(".")) { if (dirent.name.startsWith(".")) {
return false; return false;
} }
*/
var entType; var entType;
if (dirent.isDirectory()) { if (dirent.isDirectory()) {

128
create.js
View File

@ -1,10 +1,9 @@
// a port of Go's filepath.Walk // a port of Go's filepath.Walk
import { promises as fs } from "fs"; import { promises as fs } from "fs";
import { skipDir } from "./walk.js"; import Walk from "./walk.js";
import path from "path"; import path from "path";
var Walk = {};
var _withFileTypes = { withFileTypes: true }; var _withFileTypes = { withFileTypes: true };
var _noopts = {}; var _noopts = {};
@ -12,97 +11,64 @@ function pass(err) {
return 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) { Walk.create = function (opts) {
async function _walk(root, walker) { if (!opts) {
if (!opts) { opts = _noopts;
opts = _noopts; }
// 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;
} }
// Special case of the very first item, root // the first run, or if false === withFileTypes
var err; if ("string" === typeof _dirent) {
var stat = await fs.lstat(root).catch(function (e) { let _name = path.basename(path.resolve(pathname));
err = e; _dirent = await fs.lstat(pathname).catch(pass);
return null; if (_dirent instanceof Error) {
}); err = _dirent;
if (stat) { } else {
stat.name = path.basename(path.resolve(root)); _dirent.name = _name;
}
} }
/* similar to function in main walk loop */ // run the user-supplied function and either skip, bail, or continue
var uerr = await walker(err, root, stat).then(pass).catch(pass); err = await walkFunc(err, pathname, _dirent).catch(pass);
if (skipOrThrow(uerr) || err) { if (false === err || Walk.skipDir === err) {
return;
}
if (err instanceof Error) {
throw err;
}
// "walk does not follow symbolic links"
if (!_dirent || !_dirent.isDirectory()) {
return; return;
} }
if (false === opts.withFileTypes) { // lightweight dirents or full lstat
stat = stat.name; 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) { if (opts.sort) {
stat = (opts.sort([stat]) || [])[0]; result = opts.sort(result);
} }
for (let entity of result) {
if (stat && stat.isDirectory()) { await _walk(path.join(pathname, entity.name || entity), walkFunc, entity);
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;
}
// "walk does not follow symbolic links"
if (dirent.isDirectory()) {
await _walkHelper(path.join(root, dirent.name), dirent, walker, opts);
}
/* end */
}
}
}
return _walk; return _walk;
}; };

View File

@ -1,3 +1,6 @@
import Walk from "./walk.js"; import Walk from "./walk.js";
import Walk2 from "./create.js";
Walk.create = Walk2.create;
export default Walk; export default Walk;

View File

@ -6,7 +6,8 @@
"main": "index.js", "main": "index.js",
"type": "module", "type": "module",
"files": [ "files": [
"walk.js" "walk.js",
"create.js"
], ],
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1" "test": "echo \"Error: no test specified\" && exit 1"

55
snippet.js Normal file
View File

@ -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
View File

@ -6,13 +6,13 @@ const skipDir = new Error("skip this directory");
const pass = (err) => err; const pass = (err) => err;
// a port of Go's filepath.Walk // a port of Go's filepath.Walk
const walk = async (root, walkFunc, _dirent) => { const walk = async (pathname, walkFunc, _dirent) => {
let err; let err;
// special case of the very first run // special case of the very first run
if (!_dirent) { if (!_dirent) {
let _name = path.basename(path.resolve(root)); let _name = path.basename(path.resolve(pathname));
_dirent = await fs.lstat(root).catch(pass); _dirent = await fs.lstat(pathname).catch(pass);
if (_dirent instanceof Error) { if (_dirent instanceof Error) {
err = _dirent; err = _dirent;
} else { } else {
@ -21,7 +21,7 @@ const walk = async (root, walkFunc, _dirent) => {
} }
// run the user-supplied function and either skip, bail, or continue // 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) { if (false === err || skipDir === err) {
return; return;
} }
@ -33,12 +33,12 @@ const walk = async (root, walkFunc, _dirent) => {
if (!_dirent.isDirectory()) { if (!_dirent.isDirectory()) {
return; return;
} }
let result = await fs.readdir(root, _withFileTypes).catch(pass); let result = await fs.readdir(pathname, _withFileTypes).catch(pass);
if (result instanceof Error) { if (result instanceof Error) {
return walkFunc(result, root, _dirent); return walkFunc(result, pathname, _dirent);
} }
for (let dirent of result) { for (let dirent of result) {
await walk(path.join(root, dirent.name), walkFunc, dirent); await walk(path.join(pathname, dirent.name), walkFunc, dirent);
} }
}; };