How to distinguish files from directories in Node.js

By

Identify files, directories, and symbolic links in Node.js with lstatSync() or readdirSync() withFileTypes, and filter entries by their type.

~~~

A directory listing mixes files and subfolders. To decide which entries to process, inspect their type with lstatSync() or request Dirent objects with withFileTypes.

If you only need the names, start with listing a folder in Node.js. Here we’ll classify those entries and keep regular files separately.

How do you tell files and folders apart?

Call fs.lstatSync() with each entry’s full path. Its result has methods such as isDirectory(), isFile(), and isSymbolicLink():

const fs = require('node:fs')
const path = require('node:path')

const dir = '/Users/flavio/folder'
const files = fs.readdirSync(dir)

for (const file of files) {
  const stat = fs.lstatSync(path.join(dir, file))
  if (stat.isDirectory()) {
    console.log(`${file} is a folder`)
  } else if (stat.isFile()) {
    console.log(`${file} is a file`)
  } else if (stat.isSymbolicLink()) {
    console.log(`${file} is a symbolic link`)
  }
}

A common mistake

Notice the path.join(dir, file) call. The names returned by readdirSync() are bare names, without the folder they live in.

If you pass file directly to lstatSync(), Node resolves it against the current working directory. Unless you run the script from inside that exact folder, you get an ENOENT error. Joining the name with the directory path fixes it.

Also note that lstatSync() does not follow symbolic links. A symlink pointing to a folder reports isDirectory() as false, because the link itself is not a directory. To inspect the target of the link instead, use fs.statSync(), which follows it.

A shortcut: withFileTypes

You can skip the lstatSync() calls entirely. Pass the withFileTypes option and readdirSync() returns fs.Dirent objects instead of strings:

const fs = require('node:fs')

const dir = '/Users/flavio/folder'
const entries = fs.readdirSync(dir, { withFileTypes: true })

for (const entry of entries) {
  console.log(entry.name, entry.isDirectory())
}

Each entry carries its name and type, so you avoid a separate lstatSync() call for every entry. Dirent also describes a symbolic link itself, rather than its target.

To keep only regular files, use isFile():

const files = entries.filter((entry) => entry.isFile()).map((entry) => entry.name)

For subdirectories, use entry.isDirectory(). A name ending in .md alone does not prove that an entry is a file.

fs and path are built-in modules, no need to install them using npm. The node: prefix makes that explicit, and with it Node always loads its own fs module, never an npm package with the same name.

Tagged: Node.js · All topics

Want me to talk about your product? You can sponsor this site.

~~~

Related posts about node: