npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

njfs

v2.0.2

Published

Modern Node.js filesystem utilities - Zero dependencies, async-first API with recursive operations, cross-device move support, and full TypeScript JSDoc annotations.

Readme

njfs

CI npm version npm downloads license

Modern Node.js filesystem utilities - Zero dependencies, async-first API with recursive operations, cross-device move support, and full TypeScript JSDoc annotations.

Production-ready filesystem utilities with zero dependencies. Supports both files and directories, recursive operations, and cross-platform compatibility.

Features

  • ✅ Zero dependencies
  • ✅ Simple, intuitive API
  • ✅ Async-first with optional sync methods
  • ✅ Recursive directory operations
  • ✅ Cross-device move support
  • ✅ Modern Node.js support (18+)
  • ✅ Full TypeScript JSDoc annotations
  • ✅ Production-tested

Installation

npm install njfs

Quick Start

const { copy, list, mkdirp, readFile, writeFile } = require('njfs');

// Copy entire directory
await copy('./src', './dist');

// List files recursively
const files = await list('./src', { recursive: true, extensions: ['js', 'ts'] });

// Create nested directories
await mkdirp('./build/assets/images');

// Read and write files
const content = await readFile('./config.json');
await writeFile('./output/data.json', JSON.stringify(data));

API Reference

File & Directory Checking

exists(path) • Async

Check if a path exists (file or directory).

const { exists } = require('njfs');

if (await exists('./config.json')) {
  console.log('Config exists!');
}

Returns: Promise<boolean>


isFile(path) • Async

Check if a path is a file.

const { isFile } = require('njfs');

if (await isFile('./package.json')) {
  console.log('Is a file!');
}

Returns: Promise<boolean>


isDir(path) • Async

Check if a path is a directory.

const { isDir } = require('njfs');

if (await isDir('./src')) {
  console.log('Is a directory!');
}

Returns: Promise<boolean>


isFileSync(path) • Sync

Synchronous version of isFile. Use sparingly (blocks event loop).

const { isFileSync } = require('njfs');

if (isFileSync('./package.json')) {
  console.log('Is a file!');
}

Returns: boolean


isDirSync(path) • Sync

Synchronous version of isDir. Use sparingly (blocks event loop).

const { isDirSync } = require('njfs');

if (isDirSync('./src')) {
  console.log('Is a directory!');
}

Returns: boolean


Directory Operations

list(path, options) • Async

List files and directories with powerful filtering options.

const { list } = require('njfs');

// List all entries
const all = await list('./src');

// Filter by extension
const jsFiles = await list('./src', { extensions: 'js' });
const codeFiles = await list('./src', { extensions: ['js', 'ts', 'jsx'] });

// Recursive listing
const allFiles = await list('./src', { recursive: true });

// Get full paths
const paths = await list('./src', {
  recursive: true,
  fullPath: true,
  extensions: ['js', 'json'],
});

Options:

  • extensions (string | string[]): Filter by file extensions
  • recursive (boolean): Walk subdirectories (default: false)
  • fullPath (boolean): Return absolute paths instead of names (default: false)

Returns: Promise<string[]>


mkdirp(path) • Async

Create directory recursively (like mkdir -p).

const { mkdirp } = require('njfs');

// Creates all parent directories
await mkdirp('./dist/assets/images');

Returns: Promise<void>


root() • Sync

Get the current working directory.

const { root } = require('njfs');

const projectRoot = root();
console.log(projectRoot); // '/home/user/my-project'

Returns: string


File Operations

copy(source, destination) • Async

Copy files or directories recursively.

const { copy } = require('njfs');

// Copy file
await copy('./src/app.js', './dist/app.js');

// Copy file to directory (keeps name)
await copy('./src/app.js', './dist/');

// Copy entire directory recursively
await copy('./src', './dist');

// Copy directory to new location
await copy('./templates', './build/templates');

Returns: Promise<string> - Destination path


move(source, destination) • Async

Move files or directories with cross-device support.

const { move } = require('njfs');

// Move file
await move('./temp/file.js', './archive/file.js');

// Move to directory (keeps name)
await move('./temp/file.js', './archive/');

// Move entire directory
await move('./old-folder', './new-folder');

// Cross-device move (automatically falls back to copy+delete)
await move('/mnt/drive1/data', '/mnt/drive2/data');

Returns: Promise<string> - Destination path

Note: Automatically handles cross-device moves by copying then deleting.


remove(path) • Async

Remove file or directory recursively.

const { remove } = require('njfs');

// Remove file
await remove('./temp.txt');

// Remove directory and all contents
await remove('./build');

Returns: Promise<void>


readFile(path, encoding) • Async

Read file contents.

const { readFile } = require('njfs');

// Read as UTF-8 (default)
const text = await readFile('./config.json');

// Read as buffer
const buffer = await readFile('./image.png', null);

// Read with specific encoding
const latin = await readFile('./legacy.txt', 'latin1');

Parameters:

  • path (string): File path
  • encoding (string | null): Encoding (default: 'utf8', use null for buffer)

Returns: Promise<string | Buffer>


writeFile(path, content) • Async

Write content to file. Creates parent directories automatically.

const { writeFile } = require('njfs');

// Write string
await writeFile('./output/data.txt', 'Hello World');

// Write JSON
await writeFile('./config.json', JSON.stringify(config, null, 2));

// Nested path (creates directories)
await writeFile('./build/assets/data.json', content);

Returns: Promise<void>


Path Utilities

unix(path) • Sync

Convert path to Unix-style separators.

const { unix } = require('njfs');

const normalized = unix('C:\\Users\\name\\project');
// Returns: 'C:/Users/name/project'

Returns: string


Usage Examples

Build Script

const { copy, list, mkdirp, remove } = require('njfs');

async function build() {
  // Clean
  await remove('./dist');

  // Create output structure
  await mkdirp('./dist/js');
  await mkdirp('./dist/css');

  // Copy all JavaScript files
  const jsFiles = await list('./src', {
    recursive: true,
    extensions: ['js', 'jsx'],
    fullPath: true,
  });

  for (const file of jsFiles) {
    const destPath = file.replace('/src/', '/dist/js/');
    await copy(file, destPath);
  }

  console.log('Build complete!');
}

build();

Directory Sync

const { copy, list, exists, remove } = require('njfs');

async function syncDirectories(source, target) {
  // Get all files from source
  const sourceFiles = await list(source, {
    recursive: true,
    fullPath: true,
  });

  // Remove target if exists
  if (await exists(target)) {
    await remove(target);
  }

  // Copy entire directory
  await copy(source, target);

  console.log(`Synced ${sourceFiles.length} files`);
}

syncDirectories('./source', './backup');

File Organization

const { list, move, mkdirp, unix } = require('njfs');
const path = require('path');

async function organizeByExtension(sourceDir) {
  const files = await list(sourceDir, { fullPath: true });

  for (const file of files) {
    const ext = path.extname(file).slice(1) || 'no-extension';
    const destDir = `${sourceDir}/organized/${ext}`;

    await mkdirp(destDir);
    await move(file, destDir);

    console.log(`Moved: ${unix(file)} → ${ext}/`);
  }
}

organizeByExtension('./downloads');

Recursive File Search

const { list, readFile } = require('njfs');

async function searchInFiles(dir, searchText) {
  const files = await list(dir, {
    recursive: true,
    extensions: ['js', 'ts', 'json'],
    fullPath: true,
  });

  const matches = [];

  for (const file of files) {
    const content = await readFile(file);
    if (content.includes(searchText)) {
      matches.push(file);
    }
  }

  return matches;
}

const results = await searchInFiles('./src', 'TODO');
console.log(`Found ${results.length} files with TODOs`);

Backup System

const { copy, mkdirp, unix } = require('njfs');
const path = require('path');

async function createBackup(source, backupRoot) {
  const timestamp = new Date().toISOString().replace(/:/g, '-');
  const backupPath = path.join(backupRoot, timestamp);

  await mkdirp(backupPath);
  await copy(source, backupPath);

  console.log(`Backup created: ${unix(backupPath)}`);
  return backupPath;
}

createBackup('./important-data', './backups');

Gulp Integration

const { list, copy, root } = require('njfs');
const { series, watch } = require('gulp');

async function copyAssets() {
  const srcDir = `${root()}/assets`;
  const distDir = `${root()}/dist/assets`;

  await copy(srcDir, distDir);
  console.log('✓ Assets copied');
}

async function processScripts() {
  const files = await list('./src', {
    recursive: true,
    extensions: ['js', 'ts'],
  });

  console.log(`Processing ${files.length} script files...`);
  // ... your build logic
}

exports.build = series(copyAssets, processScripts);
exports.watch = () => {
  watch('./assets/**/*', copyAssets);
  watch('./src/**/*.{js,ts}', processScripts);
};

Requirements

  • Node.js >= 18.0.0

Changelog

v2.0.2 (2025)

  • 🚀 Updated config files to latest standards
  • 🚀 Improved development workflow
  • 🚀 Better code quality tools
  • 🚀 Updated SEO-friendly package description

v2.0.1 (2024)

  • Dependency updates

v2.0.0 (2024)

  • 🚀 BREAKING: Requires Node.js 18+
  • 🚀 BREAKING: isDir and isFile are now async (use isDirSync/isFileSync for sync)
  • NEW: copy now supports directories recursively
  • NEW: move now supports directories with cross-device fallback
  • NEW: list supports recursive and fullPath options
  • NEW: exists(path) - Check if path exists
  • NEW: remove(path) - Recursive delete
  • NEW: mkdirp(path) - Recursive directory creation
  • NEW: readFile(path, encoding) - Read file contents
  • NEW: writeFile(path, content) - Write file with auto-mkdir
  • 🐛 Fixed root() to use process.cwd() instead of __dirname
  • 🐛 Fixed path normalization bugs on Windows
  • 🐛 Fixed stream handling in copy to properly await completion
  • 📚 Added comprehensive JSDoc documentation
  • 🔧 Better error handling and messages
  • ⚡ Performance improvements

v1.2.5 (2021)

  • Previous stable release

Migration from v1.x

// v1.x - Sync checks
if (isDir('./src')) {
}

// v2.x - Async checks (recommended)
if (await isDir('./src')) {
}

// v2.x - Sync fallback (use sparingly)
if (isDirSync('./src')) {
}

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Troubleshooting

When you encounter a problem, please open an issue.

Credits

Special thanks to Grok AI for architecture review and suggestions.

Author

Orçun Saltık

License

MIT © Orçun Saltık