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
Maintainers
Readme
Features
- 🌲 Builds a recursive tree from one or more directory paths
- 🔗 Each node carries a
parentcircular reference for upward traversal - 📍 Each node exposes a
getPath()method to retrieve its own paths directly - 🏷️ Each node has a
typefield —PathTreeNodeKind.DirorPathTreeNodeKind.File - 📏 Each node has a
depthfield indicating its distance from the root - 👁️
fileVisibleoption includes files as leaf nodes alongside directories - 🔍 Optional
filtercallback applied at every depth, including top-level entries - ⚡
build()scans the entirebasedirectory with zero configuration - 🎛️
buildBy()accepts either a path segment array or a top-level filter function - 🗃️
usePathCacheoption cachesgetPath()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-treeifyyarn add path-treeifyQuick 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 afilterto 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 references —
parentpoints back up the tree. UseJSON.stringifyreplacers or a library likeflattedif 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); // trueCommonJS usage
const { PathTreeify, PathTreeNodeKind } = require('path-treeify');
const treeify = new PathTreeify({ base: __dirname });
const tree = treeify.build();