escalade: Ascend Parent Directories
escalade is a minimalistic and performant utility designed for synchronously or asynchronously traversing up parent directories to locate a specific file or directory. It continuously executes a provided callback function for each directory in the ancestry chain until the callback returns a truthy value, at which point the absolute path to the found item is returned, or until the system root directory is reached. The current stable version is `3.2.0`, with releases occurring a few times a year for patches, minor features, and ecosystem compatibility updates (e.g., Deno support, TypeScript resolution fixes). Its primary differentiators are its extremely small footprint (183-210 bytes gzipped) and its clear separation into synchronous and asynchronous execution modes, catering to a wide range of Node.js and Deno environments. It strictly searches direct parent directories and does not explore sibling directories of parents.
Common errors
-
TypeError: escalade(...).then is not a function
cause Attempting to use `await` or `.then()` on the synchronous `escalade/sync` function.fixEnsure you are importing the correct version of escalade. If you need asynchronous behavior, import from `'escalade'`. If you intend to use the synchronous version from `'escalade/sync'`, remove `await` keywords as it returns its value directly. -
TS2307: Cannot find module 'escalade' or its corresponding type declarations.
cause TypeScript compilation issue due to incorrect module resolution or outdated type definitions.fixEnsure your `tsconfig.json` has appropriate `moduleResolution` settings (e.g., `Node16` or `NodeNext`). If using `escalade@3.1.2`, ensure `nodenext` is configured. For `escalade@3.2.0` and newer, separate ESM/CJS type definitions are provided, which should resolve most ambiguity issues. Clear `node_modules` and reinstall if issue persists. -
Error [ERR_MODULE_NOT_FOUND]: Cannot find module 'escalade/sync' imported from ...
cause Node.js or build tool failing to resolve the `escalade/sync` subpath export, often due to an environment not fully supporting `exports` maps.fixVerify your Node.js version is compatible (>=12.17.0 for stable `exports` support). Ensure bundlers or module loaders are configured to handle `exports` maps. Updating `escalade` to the latest version (`v3.1.1` fixed some specific Node 13.x issues) can also help.
Warnings
- gotcha escalade strictly traverses direct parent directories and will not explore sibling directories of any parent. Its search scope is limited to the direct ancestral chain.
- breaking In `v3.2.0`, separate TypeScript definitions for ESM and CommonJS were introduced. Previously, only ESM definitions were shipped, which could cause tool or resolution ambiguity in certain TypeScript configurations. While a fix, it might require adjustments for existing TypeScript users.
- gotcha Users running Node.js versions 13.0 through 13.6 (inclusive) might have encountered issues with `require()` due to early, partial support of the `exports` map behavior in those specific Node.js releases. This was patched in `v3.1.1`.
- gotcha The `escalade/sync` module is not Promise-based. Attempting to `await` its return value will result in a runtime error because the function does not return a Promise.
Install
-
npm install escalade -
yarn add escalade -
pnpm add escalade
Imports
- escalade
const escalade = require('escalade');import escalade from 'escalade';
- escalade (sync version)
const escaladeSync = require('escalade'); // Wrong path import escaladeSync from 'escalade'; // Not the sync versionimport escaladeSync from 'escalade/sync';
- escalade (CommonJS)
import escalade from 'escalade';
const escalade = require('escalade');
Quickstart
import { join } from 'path';
import escalade from 'escalade';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = join(__filename, '..');
// Assuming a file structure like:
// project-root/
// package.json
// src/
// index.js
// features/
// my-feature.js
const startPath = join(__dirname, 'features', 'my-feature.js');
console.log(`Starting search from: ${startPath}`);
async function findPackageJson() {
const pkgPath = await escalade(startPath, (dir, names) => {
console.log(` Searching in: ${dir}`);
if (names.includes('package.json')) {
return 'package.json';
}
});
if (pkgPath) {
console.log(`Found package.json at: ${pkgPath}`);
const packageJson = await import(pkgPath, { assert: { type: 'json' } });
console.log(`Package name: ${packageJson.default.name}`);
} else {
console.log('package.json not found in parent directories.');
}
}
findPackageJson().catch(console.error);