CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-escalade

A tiny and fast utility to ascend parent directories until a specified condition is met

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

index.mddocs/

Escalade

Escalade is a tiny (183B to 210B) and fast utility for ascending parent directories to locate specific files or directories. It provides both asynchronous and synchronous modes, using a callback-based approach where users define custom search criteria while escalade automatically traverses up the directory tree until either a match is found or the filesystem root is reached.

Package Information

  • Package Name: escalade
  • Package Type: npm
  • Language: JavaScript
  • Installation: npm install escalade

Core Imports

Async mode (default):

import escalade from "escalade";

For CommonJS:

const escalade = require("escalade");

Sync mode:

import escalade from "escalade/sync";

For CommonJS:

const escalade = require("escalade/sync");

With TypeScript types:

import escalade from "escalade";
import escaladeSync from "escalade/sync";

// Access Callback types via namespace (due to CommonJS-style export)
type AsyncCallback = escalade.Callback;
type SyncCallback = escaladeSync.Callback;

Note: The package uses CommonJS-style exports (export =), so the Callback types are accessed via the function namespace rather than as named exports.

Basic Usage

Async mode example:

import { join } from 'path';
import escalade from 'escalade';

// Find the nearest package.json
const packagePath = await escalade(process.cwd(), (dir, files) => {
  if (files.includes('package.json')) {
    return 'package.json';  // Returns absolute path
  }
});

console.log(packagePath);
// => "/Users/project/package.json" (or undefined if not found)

Sync mode example:

import { join } from 'path';
import escalade from 'escalade/sync';

// Find the nearest .git directory
const gitDir = escalade(process.cwd(), (dir, files) => {
  return files.includes('.git') && '.git';
});

console.log(gitDir);
// => "/Users/project/.git" (or undefined if not found)

Architecture

Escalade operates using a simple traversal pattern:

  • Input Resolution: Starting path is resolved relative to process.cwd() if not absolute
  • Directory Detection: If input is a file, escalade starts from its parent directory
  • Callback Execution: For each directory level, the callback receives the current directory path and its contents
  • Termination Conditions: Stops when callback returns truthy value or filesystem root is reached
  • Path Resolution: Relative callback returns are resolved to absolute paths from the current directory

Capabilities

Async Directory Traversal

Asynchronously ascends parent directories using promises and Node.js async filesystem operations.

/**
 * Asynchronously ascend parent directories until callback returns truthy value
 * @param directory - Starting path (file or directory)
 * @param callback - Function called for each directory level
 * @returns Promise resolving to absolute path or undefined
 */
function escalade(
  directory: string,
  callback: AsyncCallback
): Promise<string | void>;

type Promisable<T> = T | Promise<T>;

type AsyncCallback = (
  directory: string,
  files: string[]
) => Promisable<string | false | void>;

Usage Example:

import escalade from 'escalade';

// Find configuration file with async callback
const configPath = await escalade(__dirname, async (dir, files) => {
  // Callback can be async
  for (const file of files) {
    if (file.endsWith('.config.js')) {
      // Verify it's actually a config file
      const content = await fs.readFile(path.join(dir, file), 'utf8');
      if (content.includes('module.exports')) {
        return file;
      }
    }
  }
});

Sync Directory Traversal

Synchronously ascends parent directories using Node.js synchronous filesystem operations.

/**
 * Synchronously ascend parent directories until callback returns truthy value
 * @param directory - Starting path (file or directory)
 * @param callback - Function called for each directory level
 * @returns Absolute path or undefined
 */
function escalade(
  directory: string,
  callback: SyncCallback
): string | void;

type SyncCallback = (
  directory: string,
  files: string[]
) => string | false | void;

Usage Example:

import escalade from 'escalade/sync';

// Find nearest license file
const licensePath = escalade(process.cwd(), (dir, files) => {
  const licenseFile = files.find(file => 
    file.toLowerCase().startsWith('license')
  );
  return licenseFile || false;
});

Callback Function Behavior

The callback function controls the search behavior:

  • Parameters:
    • directory (string): Absolute path of current directory being searched
    • files (string[]): Array of file and directory names in current directory (not paths)
  • Return Values:
    • Truthy string: Stops search, returns resolved absolute path
    • Falsy value (false, undefined, null): Continues to parent directory
    • Absolute path: Returned as-is without resolution
    • Relative path: Resolved relative to current directory

Advanced callback example:

const found = await escalade('/some/deep/path', (dir, files) => {
  console.log(`Searching in: ${dir}`);
  console.log(`Contents: ${files.join(', ')}`);
  
  // Multiple search criteria
  if (files.includes('package.json')) {
    return 'package.json';
  }
  
  if (files.includes('yarn.lock')) {
    return 'yarn.lock';
  }
  
  // Continue searching
  return false;
});

Error Handling

Escalade relies on Node.js filesystem operations and will throw standard filesystem errors:

  • ENOENT: Directory or file doesn't exist
  • EACCES: Permission denied
  • ENOTDIR: Path component is not a directory

Error handling example:

try {
  const result = await escalade('/nonexistent/path', (dir, files) => {
    return files.includes('target.txt') && 'target.txt';
  });
} catch (error) {
  if (error.code === 'ENOENT') {
    console.log('Starting path does not exist');
  } else if (error.code === 'EACCES') {
    console.log('Permission denied accessing directory');
  } else {
    console.log('Unexpected error:', error.message);
  }
}

Platform Support

  • Node.js: >=6 (sync mode), >=8 (async mode)
  • Deno: Supported via https://deno.land/x/escalade
  • Bundle Size: 183B (sync), 210B (async) when gzipped
  • Dependencies: Zero runtime dependencies

Install with Tessl CLI

npx tessl i tessl/npm-escalade

docs

index.md

tile.json