スーパーマン
スーパーマン

Reputation: 135

Get all directories recursive within directory with nodejs

I want to get all directories synchronize within a given directory.

<MyFolder>
    |- Folder1
       |- Folder11
       |- Folder12
    |- Folder2
    |- File1.txt
    |- File2.txt
    |- Folder3
        |- Folder31
        |- Folder32

I would expect to get an array of:

["Folder1/Folder11", "Folder1/Folder12", "Folder2", "Folder3/Folder31", "Folder3/Folder32"]

This is my code:

const fs = require('fs');
const path = require('path');

function flatten(lists) {
  return lists.reduce((a, b) => a.concat(b), []);
}

function getDirectories(srcpath) {
  return fs.readdirSync(srcpath)
  .map(file => path.join(srcpath, file))
  .filter(path => fs.statSync(path).isDirectory());
}

function getDirectoriesRecursive(srcpath) {
  return [srcpath, ...flatten(getDirectories(srcpath).map(getDirectoriesRecursive))];
}

Would anyone help me solve the problem above?

Upvotes: 1

Views: 372

Answers (2)

Mulan
Mulan

Reputation: 135217

async

Here's a highly optimised version using Node's fast fs.Dirent objects. This approach allows you to skip the expensive fs.existsSync and fs.statSync calls on every path -

const { readdir } =
  require ("fs/promises")

const { join } =
  require ("path")

const dirs = async (path = ".") =>
  Promise.all
    ( (await readdir (path, { withFileTypes: true }))
        .map
          ( dirent =>
              dirent .isDirectory ()
                ? dirs (join (path, dirent.name))
                : []
          )
    )
    .then
      ( results =>
          [] .concat (path, ...results)
      )

You use it like this -

dirs ("MyFolder") .then (console.log, console.error)

sync

We can rewrite the above function using synchronous functions instead -

const { readdirSync } =
  require ("fs")

const { join } =
  require ("path")

const dirsSync = (path = ".") =>
  [].concat
    ( path
    , ...readdirSync (path, { withFileTypes: true })
        .map
          ( dirent =>
              dirent .isDirectory ()
                ? dirsSync (join (path, dirent.name))
                : []
          )
    )

You use it like this -

console .log (dirsSync ("MyFolder"))

This can be further simplified by using Array.prototype.flatMap -

const { readdirSync } =
  require ("fs")

const { join } =
  require ("path")

const dirsSync = (path = ".") =>
  [ path
  , ...readdirSync (path, { withFileTypes: true })
      .flatMap
        ( dirent =>
            dirent .isDirectory ()
              ? dirsSync (join (path, dirent.name))
              : []
        )
  ]

Upvotes: 2

cнŝdk
cнŝdk

Reputation: 32145

You were almost there, you just need to avoid files (not directories), so the getDirectoriesRecursive(srcpath) function recursion won't throw an error.

This is how should be the final getDirectoriesRecursive code:

function getDirectoriesRecursive(srcpath) {
  return [srcpath, ...flatten(getDirectories(srcpath).map((p) => {
    try {
      if (fs.existsSync(path)) {
        if (fs.lstatSync(path).isDirectory()) {
          return getDirectoriesRecursive(path);
        }
      }
    } catch (err) {
      console.error(err)
    }
  }).filter(p => p !== undefined))];
}

This is a live working Repl Demo to show all directories inside "/opt".

Upvotes: 0

Related Questions