I accidentally typed await(await stat(content...
and it worked. Not sure if this is valid syntax, or there is a better way to do it? I’m trying to read all the files that are directories and do not match my regex.
const fs = require('fs') const path = require('path') const content = path.resolve('.') + '/docs' + '/' const util = require('util'); const stat = util.promisify(fs.stat) const readDir = util.promisify(fs.readdir) const directories = 'docs/'; const exclude = new RegExp(/^(adir|.somedir)/,'i'); let newFiles = {} async function main(){ const ls = await readDir(directories) console.log('starting....'); let newArray = [] for (let index = 0; index < ls.length; index++) { let x = await (await stat(content + ls[index])).isDirectory() let file = ls[index] if (x && !(exclude.test(file))){newArray.push(file)} console.log('x is ',x); } console.log('new filtered array: ', newArray); }
Advertisement
Answer
ls
My advice would be not to put all of your eggs in one basket. We can write an ultra fast ls
function using Node’s fs.Dirent objects and bypass the need for a slow fs.stat
call on each file –
// fsext.js import { readdir } from "fs/promises" import { join } from "path" async function* ls (path = ".") { yield { dir: path } for (const dirent of await readdir(path, { withFileTypes: true })) if (dirent.isDirectory()) yield *ls(join(path, dirent.name)) else yield { file: join(path, dirent.name) } } async function toArray (iter) { const r = [] for await (const v of iter) r.push(v) return r } export { ls, toArray }
// main.js import { ls, toArray } from "./fsext.js" toArray(ls("./node_modules")).then(console.log, console.error)
To test it out, let’s add some popular npm
packages so we have a large hierarchy to test our our program. We’ll install the lot and count the number of directories and files –
$ npm install async chalk commander debug express immutable lodash moment prop-types react react-dom request webpack $ find ./node_modules | wc -l
5453
Now let’s run our program and time
it –
$ time node main.js
[ { dir: './node_modules' }, { dir: 'node_modules/.bin' }, { file: 'node_modules/.bin/acorn' }, { file: 'node_modules/.bin/browserslist' }, { file: 'node_modules/.bin/loose-envify' }, { file: 'node_modules/.bin/mime' }, { file: 'node_modules/.bin/sshpk-conv' }, { file: 'node_modules/.bin/sshpk-sign' }, { file: 'node_modules/.bin/sshpk-verify' }, { file: 'node_modules/.bin/terser' }, { file: 'node_modules/.bin/uuid' }, { file: 'node_modules/.bin/webpack' }, { file: 'node_modules/.package-lock.json' }, { dir: 'node_modules/@types' }, { dir: 'node_modules/@types/eslint' }, { file: 'node_modules/@types/eslint/LICENSE' }, { file: 'node_modules/@types/eslint/README.md' }, { file: 'node_modules/@types/eslint/helpers.d.ts' }, { file: 'node_modules/@types/eslint/index.d.ts' }, { dir: 'node_modules/@types/eslint/lib' }, ... 5433 more items ]
node main.js 0.09s user 0.02s system 116% cpu 0.099 total
dirs
If we only want directories, we can write dirs
as a simple specialization of our generic ls
–
// fsext.js (continued) async function* dirs (path) { for await (const f of ls(path)) if (f.dir) yield f.dir }
$ find ./node_modules -type d | wc -l
457
Now compare it against our program
// main.js import { dirs, toArray } from "./fsext.js" toArray(dirs("./node_modules")).then(console.log, console.error)
$ time node.main.js
[ './node_modules', 'node_modules/.bin', 'node_modules/@types', 'node_modules/@types/eslint', 'node_modules/@types/eslint/lib', 'node_modules/@types/eslint/lib/rules', 'node_modules/@types/eslint/rules', 'node_modules/@types/eslint-scope', 'node_modules/@types/estree', 'node_modules/@types/json-schema', 'node_modules/@types/node', 'node_modules/@types/node/assert', 'node_modules/@types/node/dns', 'node_modules/@types/node/fs', 'node_modules/@types/node/stream', 'node_modules/@types/node/timers', 'node_modules/@types/node/ts3.6', 'node_modules/@webassemblyjs', 'node_modules/@webassemblyjs/ast', 'node_modules/@webassemblyjs/ast/esm', ... 437 more items ]
node main2.js 0.09s user 0.02s system 108% cpu 0.099 total
exclude
If we want to exclude
certain directories or files, we can write it generically as well –
// fsext.js (continued) async function* exclude (iter, test) { for await (const v of iter) if (Boolean(test(v))) continue else yield v }
// main.js import { dirs, exclude, toArray } from "./fsext.js" toArray(exclude(dirs("./node_modules"), v => /@/.test(v))) .then(console.log, console.error)
$ time node main.js
[ './node_modules', 'node_modules/.bin', 'node_modules/accepts', 'node_modules/acorn', 'node_modules/acorn/bin', 'node_modules/acorn/dist', 'node_modules/ajv', 'node_modules/ajv/dist', 'node_modules/ajv/lib', 'node_modules/ajv/lib/compile', 'node_modules/ajv/lib/dot', 'node_modules/ajv/lib/dotjs', 'node_modules/ajv/lib/refs', 'node_modules/ajv/scripts', 'node_modules/ajv-keywords', 'node_modules/ajv-keywords/keywords', 'node_modules/ajv-keywords/keywords/dot', 'node_modules/ajv-keywords/keywords/dotjs', 'node_modules/ansi-styles', 'node_modules/array-flatten', ... 351 more items ]
node main.js 0.09s user 0.02s system 105% cpu 0.104 total
reorganize
In our file system extensions module, fsext
, we wrote two functions that work on any iterables, not just the ls
or dirs
. I would suggest breaking these out into their own iter
module. This type of reorganization helps decouple concerns and maximize code reuse throughout your entire program –
// iter.js async function* empty () {} async function* exclude (iter = empty(), test = Boolean) { for await (const v of iter) if (Boolean(test(v))) continue else yield v } async function toArray (iter = empty()) { const r = [] for await (const v of iter) r.push(v) return r } export { empty, exclude, toArray }
// fsext.js import { readdir } from "fs/promises" import { join } from "path" async function* ls (path = ".") { yield { dir: path } for (const dirent of await readdir(path, { withFileTypes: true })) if (dirent.isDirectory()) yield *ls(join(path, dirent.name)) else yield { file: join(path, dirent.name) } } async function* dirs (path) { for await (const f of ls(path)) if (f.dir) yield f.dir } async function* files (path) { for await (const f of ls(path)) if (f.file) yield f.file } export { ls, dirs, files }
// main.js import { dirs } from "./fsext.js" import { exclude, toArray } from "./iter.js" const somePath = "..." const someTest = v => ... toArray(exclude(dirs(somePath), someTest)) .then(console.log, console.error)
search
Looking for a specific file or folder? Read on in this Q&A to implement search
.