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

path-treeify

v1.4.0

Published

Convert a path or an array of paths into a tree-structured JavaScript object, where each node has a `parent` property that holds a circular reference to its parent node.

Downloads

1,714

Readme

📖 中文文档 (Chinese README)


Features

  • 🌲 Builds a recursive tree from one or more directory paths
  • 🔗 Each node carries a parent circular reference for upward traversal
  • 📍 Each node exposes a getPath() method to retrieve its own paths directly
  • 🏷️ Each node has a type field — PathTreeNodeKind.Dir or PathTreeNodeKind.File
  • 📏 Each node has a depth field indicating its distance from the root
  • 👁️ fileVisible option includes files as leaf nodes alongside directories
  • 🔍 Optional filter callback applied at every depth, including top-level entries
  • build() scans the entire base directory with zero configuration
  • 🎛️ buildBy() accepts either a path segment array or a top-level filter function
  • 🗃️ usePathCache option caches getPath() results per node for repeated access
  • 📦 Ships as both ESM (index.mjs) and CJS (index.cjs) with full TypeScript types
  • 🚫 Zero runtime dependencies

Requirements

  • Node.js >= 18.0.0

Installation

npm install path-treeify
yarn add path-treeify

Quick Start

import { PathTreeify, PathTreeNodeKind } from 'path-treeify';

// Directories only (default)
const treeify = new PathTreeify({ base: '/your/project/root' });
const tree = treeify.build();

// Include files as leaf nodes
const treeifyWithFiles = new PathTreeify({
  base: '/your/project/root',
  fileVisible: true,
});
const fullTree = treeifyWithFiles.build();

// Check node type and depth
for (const child of tree.children) {
  if (child.type === PathTreeNodeKind.Dir) {
    console.log(`dir (depth ${child.depth}):`, child.value);
  } else if (child.type === PathTreeNodeKind.File) {
    console.log(`file (depth ${child.depth}):`, child.value);
  }
}

API

new PathTreeify(options)

Creates a new instance.

| Option | Type | Required | Description | |----------------|------------------------------|----------|-------------------------------------------------------------------------------------| | base | string | ✅ | Absolute path to the root directory to scan from | | filter | FilterFunction (see below) | ❌ | Applied at every depth — top-level entries included | | fileVisible | boolean | ❌ | When true, files are included as leaf nodes. Defaults to false | | usePathCache | boolean | ❌ | When true, getPath() results are cached on each node after the first call |

base must exist and be a directory, otherwise the constructor throws.


FilterFunction

Used as the filter option in the constructor. Applied at every level of the tree, including the immediate children of base.

type FilterFunction = (params: {
  name: string;    // entry name (file or directory name)
  dirPath: string; // absolute path of the parent directory
}) => boolean;

Return true to include the entry; false to skip it entirely.

Example — exclude node_modules and hidden entries at every depth:

const treeify = new PathTreeify({
  base: '/your/project',
  filter: ({ name }) => name !== 'node_modules' && !name.startsWith('.'),
});

build(): PathTreeNode

Scans all entries directly under base that pass the instance-level filter and returns a synthetic root PathTreeNode. When fileVisible is true, files are included as leaf nodes.

const tree = treeify.build();

buildBy(segments: string[]): PathTreeNode

Builds a tree from the given list of relative path segments. When fileVisible is true, file paths are also accepted.

const tree = treeify.buildBy(['src', 'docs', 'tests']);

// With fileVisible: true, files can also be specified
const treeWithFiles = new PathTreeify({ base: '/your/project', fileVisible: true });
treeWithFiles.buildBy(['src', 'README.md']);
  • Both / and \ separators are normalised automatically.
  • Leading/trailing slashes and empty segments are stripped.
  • Throws if any segment does not resolve to a valid entry under base.

buildBy(filter: (segment: string) => boolean): PathTreeNode

Collects all top-level entries under base, applies the predicate to select which ones to include, then builds a tree. The instance-level filter still applies during deep traversal.

const tree = treeify.buildBy(name => name !== 'node_modules' && !name.startsWith('.'));

Note: the predicate passed to buildBy(fn) only selects which top-level entries to include. To filter entries at every depth, pass a filter to the constructor.


PathTreeNode

PathTreeNode is an interface — each node exposes a getPath() method to retrieve its paths without needing the PathTreeify instance.

interface PathTreeNode {
  depth:    number;               // distance from root; root is 0, its children are 1, etc.
  parent:   PathTreeNode | null;  // null only on the synthetic root
  value:    string;               // entry name for this node
  children: PathTreeNode[];       // empty for file nodes
  type:     PathTreeNodeKind;     // Dir, File, or Unknown

  getPath(): { relative: string; absolute: string };
}

When usePathCache: true is set on the PathTreeify instance, the result of getPath() is cached on the node after the first call — subsequent calls return the same object reference without recomputing the parent chain.

⚠️ Circular referencesparent points back up the tree. Use JSON.stringify replacers or a library like flatted if you need to serialize the result.


PathTreeNodeKind

An enum classifying each node's filesystem type.

enum PathTreeNodeKind {
  Dir     = 'dir',
  File    = 'file',
  Unknown = 'unknown', // assigned before the type is resolved
}

Examples

Directories only (default)

import { PathTreeify } from 'path-treeify';

const treeify = new PathTreeify({ base: '/your/project' });
const tree = treeify.build();

Include files as leaf nodes

const treeify = new PathTreeify({
  base: '/your/project',
  fileVisible: true,
});
const tree = treeify.build();

Exclude directories at every depth via constructor filter

const treeify = new PathTreeify({
  base: '/your/project',
  filter: ({ name }) => name !== 'node_modules' && !name.startsWith('.'),
});
const tree = treeify.build();

Scan specific paths

const tree = treeify.buildBy(['src', 'tests', 'docs']);

Select top-level entries with a predicate

const tree = treeify.buildBy(name => name !== 'node_modules' && !name.startsWith('.'));

Retrieve paths via node.getPath()

function printPaths(node) {
  for (const child of node.children) {
    const { absolute } = child.getPath();
    console.log(`[${child.type}] depth=${child.depth} ${absolute}`);
    printPaths(child);
  }
}

printPaths(tree);

Cache getPath() results for repeated traversal

const treeify = new PathTreeify({
  base: '/your/project',
  usePathCache: true,
});
const tree = treeify.build();

// First call walks the parent chain and caches the result
const pathA = tree.children[0].getPath();
// Subsequent calls return the cached object directly
const pathB = tree.children[0].getPath();
console.log(pathA === pathB); // true

CommonJS usage

const { PathTreeify, PathTreeNodeKind } = require('path-treeify');

const treeify = new PathTreeify({ base: __dirname });
const tree = treeify.build();

License

MIT © isaaxite