A powerful TypeScript library to parse ls
and ls -R
output into structured JSON format with advanced querying capabilities.
- 🚀 Zero Dependencies - Pure TypeScript implementation with no external dependencies for maximum compatibility and minimal bundle size
- 📁 Standard ls parsing - Parse regular
ls -l
output into structured data - 🔄 Recursive ls parsing - Parse
ls -R
output with powerful traversal methods - ⚡ Streaming support - Process large ls outputs efficiently
- 🎯 Advanced querying - Find, filter, and traverse directory structures
- 🔍 Depth control - Limit recursive parsing depth for performance
- 🛡️ Error handling - Graceful handling of permission denied and malformed entries
npm install ls-json
Or using pnpm:
pnpm add ls-json
import { parse } from "ls-json";
const lsOutput = `total 160
drwxr-xr-x 12 user staff 384 Sep 19 14:51 .
drwxr-xr-x 9 user staff 288 Sep 19 14:16 ..
drwxr-xr-x 7 user staff 224 Sep 19 14:35 dist
-rw-r--r-- 1 user staff 2640 Sep 19 14:51 index.spec.ts`;
const result = parse(lsOutput);
console.log(JSON.stringify(result, null, 2));
Output:
[
{
"filename": "dist",
"flags": "drwxr-xr-x",
"mode": "755",
"type": "directory",
"owner": "user",
"group": "staff",
"size": 224,
"date": "Sep 19 14:35",
"links": 7
},
{
"filename": "index.spec.ts",
"flags": "-rw-r--r--",
"mode": "644",
"type": "file",
"owner": "user",
"group": "staff",
"size": 2640,
"date": "Sep 19 14:51",
"links": 1
}
]
import { parse } from "ls-json";
const recursiveOutput = `./src:
total 12
-rw-r--r-- 1 user user 256 Sep 19 14:51 index.ts
drwxr-xr-x 2 user user 4096 Sep 19 14:52 utils
./src/utils:
total 8
-rw-r--r-- 1 user user 128 Sep 19 14:53 helper.ts`;
const result = parse(recursiveOutput, { recursive: true });
// Get all files across all directories
const allFiles = result.getAllEntries();
console.log(allFiles.length); // 3 entries
// Find specific files
const indexFile = result.findEntry((entry) => entry.filename === "index.ts");
console.log(indexFile?.parent); // "./src"
// Filter by file type
const jsFiles = result.filterEntries(
(entry) => entry.filename.endsWith(".ts") && entry.type === "file"
);
console.log(jsFiles.length); // 2 TypeScript files
The main parsing function that handles both standard and recursive ls output.
Parameters:
data
- The ls command output stringoptions
- Optional configuration object
Returns:
LsEntry[]
for standard parsingLsRecursiveOutput
for recursive parsing (whenoptions.recursive = true
)
Process ls output line by line using a generator for memory efficiency.
interface ParseOptions {
// Parse as recursive ls output (ls -R)
recursive?: boolean;
// Maximum directory depth (0 = root only, undefined = no limit)
depth?: number;
// Include . and .. entries (default: false)
showDotsDir?: boolean;
// Return raw data without processing (default: false)
raw?: boolean;
// Continue on errors (default: false)
ignoreExceptions?: boolean;
}
When using recursive: true
, the result object provides powerful traversal methods:
// Get all entries from all directories
const allEntries = result.getAllEntries();
// Find first entry matching criteria
const configFile = result.findEntry(
(entry) => entry.filename === "config.json"
);
// Get all entries matching criteria
const largeFiles = result.filterEntries((entry) => entry.size > 1000000);
// Get entries from specific directory
const homeEntries = result.getEntriesByPath("/home/user");
// Get directory tree structure
const tree = result.getDirectoryTree(); // { '/path': [entries...] }
// Process all entries with callback
result.traverse((entry, directory) => {
console.log(`${directory.path}/${entry.filename}`);
});
// Only parse root level + 1 level deep
const result = parse(recursiveOutput, {
recursive: true,
depth: 1,
});
// Process shallow structure for better performance
const topLevelDirs = result.filterEntries(
(entry) => entry.type === "directory"
);
const result = parse(lsRecursiveOutput, { recursive: true });
// Find the largest file
const largestFile = result
.getAllEntries()
.filter((entry) => entry.type === "file" && typeof entry.size === "number")
.reduce((largest, current) =>
current.size > largest.size ? current : largest
);
// Count files by extension
const extensionCounts = {};
result.traverse((entry) => {
if (entry.type === "file") {
const ext = entry.filename.split(".").pop() || "no-extension";
extensionCounts[ext] = (extensionCounts[ext] || 0) + 1;
}
});
// Find executable files
const executables = result.filterEntries(
(entry) => entry.flags && entry.flags.includes("x") && entry.type === "file"
);
const result = parse(lsOutput, {
recursive: true,
ignoreExceptions: true,
});
// Check for parsing errors
if (result.errors) {
console.log("Global errors:", result.errors);
}
// Check directory-specific errors
result.directories.forEach((dir) => {
if (dir.errors) {
console.log(`Errors in ${dir.path}:`, dir.errors);
}
});
interface LsEntry {
filename: string; // File or directory name
flags: string; // Permission string (e.g., "-rwxr-xr-x")
mode: string; // Octal permissions (e.g., "755")
type:
| "file"
| "directory"
| "symlink"
| "block"
| "character"
| "pipe"
| "socket";
owner: string; // Owner username
group: string; // Group name
size: number | string; // Size in bytes or human-readable
date: string; // Modification date
links?: number; // Hard link count
parent?: string; // Parent directory (recursive mode only)
link_to?: string; // Symlink target
epoch?: number; // Unix timestamp (when available)
}
- Use
depth
parameter to limit recursive parsing depth - Use
parseStreaming
for very large outputs - Set
ignoreExceptions: true
for malformed input - Use specific query methods instead of
getAllEntries()
when possible