find-up
Find a file or directory by walking up (or down) parent directories from any path.
Repository Health
Technical Analysis
find-up is a small, dependency-light Node.js library from Sindre Sorhus for locating a file or directory by walking up parent directories from a starting point, similar to how Node itself resolves node_modules. It exposes both async and sync APIs (findUp/findUpSync), a multi-match variant that returns every match up the tree (findUpMultiple/findUpMultipleSync), and a companion findDown/findDownSync pair added in v8 for searching descendant directories instead of ancestors.
Under the hood it delegates the per-directory existence check to its sibling package locate-path, so find-up’s own code is just the directory-walking loop, stop conditions, and matcher-function support. Callers can pass a plain filename, an array of candidate names (first match wins, in order), or a custom async/sync matcher function that receives each directory and can return a path, undefined to keep walking, or the findUpStop symbol to abort the search early — useful as a performance guard when the current working directory is deeply nested.
Because it’s ESM-only, has zero runtime dependencies beyond locate-path and unicorn-magic, and requires Node 20+, it’s commonly reached for when writing CLI tools, linters, and build tooling that need to discover a config file, a monorepo root, or a package.json relative to wherever the user invoked the command from.
What You Get
findUp/findUpSync— return the first matching path found while walking up fromcwd, orundefinedif nothing matches before hittingstopAt(default: the filesystem root)findUpMultiple/findUpMultipleSync— return every matching path found across all visited ancestor directories, optionally capped with alimitoptionfindDown/findDownSync— walk descendant directories instead, with a configurabledepthand a choice of breadth-first or depth-firststrategy- Matcher-function support — pass a function instead of a name to run custom logic per directory and return a path,
undefined, or the exportedfindUpStopsymbol to abort early typeoption (file|directory|both) andallowSymlinksto control exactly what counts as a match- Full TypeScript type definitions (
index.d.ts) shipped in the package, validated against real usage viatsd
Common Use Cases
- Config file discovery - CLI tools and linters call
findUp(['app.config.js', 'app.config.json'])to locate a project’s config file starting from wherever the user ran the command. - Monorepo/package root resolution - build tooling uses
findUp('package.json')or the matcher-function form to find the nearest (or a specific)package.json/workspace root relative to a file being processed. - Git repository root detection - tools that need to know if they’re inside a Git repo use
findUp('.git', {type: 'both'})to handle both regular repos and submodules, where.gitcan be a file instead of a directory. - Stopping search at a known boundary - passing
stopAtlets a tool avoid walking past a known project boundary (e.g. the invoking user’s home directory) when looking for a file that may not exist. - Finding the closest match among children -
findDown(new in v8) searches a directory’s descendants rather than its ancestors, useful for locating a nested entry point or fixture file below a known root.
Under The Hood
Architecture
The entire module is a single flat index.js file with no internal layering: findUpMultiple/findUpMultipleSync implement the core upward-walk loop (resolve cwd, compute a stopAt boundary, repeatedly call a runMatcher closure and path.dirname until the root or a limit is hit), and findUp/findUpSync are thin wrappers that call the multiple-variant with limit: 1. The newer findDown/findDownSync pair reuses the same locatePath/locatePathSync primitive but walks fs.readdir entries recursively, offering breadth-first (queue-based) and depth-first (recursive) strategies behind a shared prepareFindDownOptions helper. The only external dependency actually doing work is locate-path, to which all per-directory existence/type checks are delegated — find-up’s own code is purely the walking and stopping logic, so changing the core loop would require touching all four public entry points identically since they share the same helpers.
Tech Stack
Plain Node.js (>=20 required) with ESM-only exports ("type": "module"), zero build step — index.js ships directly, with hand-written index.d.ts type definitions rather than a TypeScript compilation step. Runtime dependencies are minimal: locate-path (path-existence checks with type/symlink filtering) and unicorn-magic (a toPath helper for normalizing URL-or-string cwd inputs). Dev tooling is Sindre Sorhus’s standard set: xo for linting, ava as the test runner, and tsd for type-definition testing, wired together in a single npm test script and run in GitHub Actions across Node 20/24 on Ubuntu and macOS.
Code Quality
The test suite (test/findup.js, test/finddown.js) is extensive relative to the module’s size — well over 60 individual ava test cases covering sync/async parity, symlink handling, custom cwd types (string, relative, URL), stopAt boundaries, matcher-function throw/reject/early-stop behavior, and type: 'both' semantics, all exercised against a real temporary-directory fixture tree rather than mocks. Error handling is minimal but intentional — matcher-function exceptions propagate to the caller rather than being swallowed, and findDown’s recursive directory reads catch and ignore readdir errors (e.g. permission-denied) rather than failing the whole search. Naming is consistent and the code has almost no inline comments, relying instead on the shipped TypeScript definitions and README for documentation. CI runs the full lint/test/type-check pipeline on every push and PR.
What Makes It Unique
find-up’s main technical distinction is offering symmetric up-and-down traversal (findUp/findDown) with matching sync variants and a shared type/symlink-matching contract, plus first-class support for passing a custom matcher function rather than only static filenames — including a dedicated findUpStop sentinel for early termination, which most comparable file-discovery utilities in the ecosystem don’t expose. It intentionally keeps the surface area and dependency graph small rather than pursuing novel algorithmic approaches, favoring predictability and composability (it explicitly recommends combining findUp and findDown for monorepo-root detection in its own README) over expanded scope.
Used by 8 apps in this directory
Continue
Developer Tools · AI Development · AI Code Assistants
Open-source coding agent for VS Code, JetBrains, and CLI with support for 30+ LLM providers.
Convex Backend
Developer Tools · Databases
Open-source reactive database that lets developers build live-updating apps with pure TypeScript, strong consistency, and real-time subscriptions—no separate API layer required.
Fern
Developer Tools
Fern turns a single OpenAPI, AsyncAPI, or Protobuf definition into type-safe SDKs for nine languages and a hosted API documentation site, all from one CLI and one source of truth.
Logto
Authentication
Open-source auth infrastructure for SaaS and AI apps with OIDC, SAML, and RBAC
melty
Developer Tools · AI Code Assistants · Code Editors
The AI code editor where every chat message is a git commit you can revert, branch, or squash
Trigger.dev
Automation · AI Development · Developer Tools
Build and deploy fully-managed AI agents and background jobs in TypeScript — with no timeouts, durable retries, real-time observability, and elastic scaling built in.
Void
AI Code Assistants · Code Editors · Automation
Open-source AI code editor with direct LLM integration and data privacy
Webiny JS
Ecommerce · Blogging · CMS
Open-source, self-hosted CMS on AWS serverless — a TypeScript framework you extend with code, not a product you configure through a UI.