A lightweight, stateful, data-structure agnostic prefix manager for rendering tree-view hierarchies.
To install the package, run:
npm install tree-stack # or deno add @lcweden/tree-stackA simple example rendering a directory with metadata annotations:
.
├── src/
│ ├── index.js
│ │ Size: 1.2 KB
│ └── utils.js
├── package.json
└── README.md
TreeStack uses an imperative, stack-based approach to generate tree prefixes.
Call enter() to go deeper into a subtree, next() to move to sibling nodes,
and leave() to return to the parent level:
import TreeStack from "tree-stack";
const stack = new TreeStack();
console.log(".");
stack.enter();
console.log(stack.node + "src/");
stack.enter();
console.log(stack.node + "index.js");
console.log(stack.lead + "Size: 1.2 KB");
stack.next({ last: true });
console.log(stack.node + "utils.js");
stack.leave();
stack.next();
console.log(stack.node + "package.json");
stack.next({ last: true });
console.log(stack.node + "README.md");
stack.leave();Tree Stack acts as a finite state machine managing ancestor lines.
Manage the hierarchy level and sibling state using stack navigation methods:
| Method | Parameters | Description |
|---|---|---|
| enter | Enters a new nesting level. | |
| leave | Exits the current nesting level. | |
| next | { last?: boolean } |
Updates the current level's completion status. Set last: true for the final child of the group. |
Access formatting prefixes and state for the current node:
| Getter | Type | Description |
|---|---|---|
| node | string |
Returns the branch prefix for the current item. |
| lead | string |
Returns the continuation guide line for multi-line content or metadata. |
| depth | number |
Returns the current nesting depth of the stack. |
Distinguishing between stack.node and stack.lead makes it simple to render
entries alongside multi-line properties, notes, or metadata without breaking
hierarchy lines:
Order
├── 🍵 Mocha <-- stack.node: intermediate item
│ 💰 Discount: 10% <-- stack.lead: continuation guide
│ 🗒️ Notes: <-- stack.lead: continuation guide
│ ├── 🍬 30% Sugar <-- stack.node: intermediate item
│ └── 🧊 Less Ice <-- stack.node: last child
├── 🧋 Milk Tea <-- stack.node: intermediate item
└── ☕️ Americano <-- stack.node: last child
The most common use case is recursively printing a tree-like data structure:
import TreeStack from "tree-stack";
const tree = {
name: "my-project",
children: [
{ name: "src", children: [{ name: "index.js" }, { name: "utils.js" }] },
{ name: "package.json" },
{ name: "README.md" },
],
};
function print(node, stack = new TreeStack()) {
const prefix = stack.depth === 0 ? "" : stack.node;
console.log(prefix + node.name);
if (!node.children || node.children.length === 0) {
return;
}
stack.enter();
node.children.forEach((child, index) => {
const last = index === node.children.length - 1;
stack.next({ last });
print(child, stack);
});
stack.leave();
}
print(tree);Output:
my-project
├── src
│ ├── index.js
│ └── utils.js
├── package.json
└── README.md
Tip
TreeStack is data-structure agnostic, not limited to tree structure.
Rendering hierarchical CLI output where items contain multi-line details or
notes, using stack.lead to keep vertical guidelines unbroken:
import TreeStack from "tree-stack";
const suite = {
name: "AuthService",
tests: [
{
name: "should hash password securely",
status: "pass",
duration: "12ms",
},
{
name: "should reject invalid email format",
status: "fail",
details: ["Expected: false", "Received: true", "at auth.test.ts:42"],
},
{
name: "should issue JWT on valid credentials",
status: "pass",
duration: "45ms",
},
],
};
const stack = new TreeStack();
console.log(suite.name);
stack.enter();
suite.tests.forEach((test, index) => {
const last = index === suite.tests.length - 1;
stack.next({ last });
const icon = test.status === "pass" ? "✔" : "✖";
console.log(`${stack.node}${icon} ${test.name}`);
if (test.duration) {
console.log(`${stack.lead}Duration: ${test.duration}`);
}
if (test.details) {
test.details.forEach((line) => {
console.log(`${stack.lead}${line}`);
});
}
});
stack.leave();Output:
AuthService
├── ✔ should hash password securely
│ Duration: 12ms
├── ✖ should reject invalid email format
│ Expected: false
│ Received: true
│ at auth.test.ts:42
└── ✔ should issue JWT on valid credentials
Duration: 45ms
You can initialize a new Tree Stack with custom characters:
| Property | Default | Description |
|---|---|---|
| tee | "├── " |
Branch prefix for intermediate items |
| corner | "└── " |
Branch prefix for the last sibling item |
| vertical | "│ " |
Vertical guideline for unclosed branches |
| blank | " " |
Blank padding when an ancestor branch has closed |
Override the default branch glyphs by passing an options object to the constructor:
new TreeStack({ tee: "├── ", corner: "└── ", vertical: "│ ", blank: " " });Pre-configure characters to suit different environments:
For environments without Unicode box-drawing support:
root
+-- child 1
| \-- grandchild
\-- child 2
Uses curved corners:
root
├── child 1
│ ╰── grandchild
╰── child 2
This project is licensed under the MIT License.