find-pkg
find-pkg is a tiny CommonJS utility that starts at a directory, walks toward the filesystem root, and returns the path of the first package.json it encounters. It exposes promise, callback, and synchronous forms, plus an optional search-depth limit. It finds the manifest path only: it does not read JSON, resolve an installed dependency, search downward, or accept arbitrary filenames and glob patterns.
A focused compatibility utility that still works for old CommonJS tooling, but its frozen 2018 surface and missing types make it a poor new dependency. Current projects should usually use find-up or pkg-dir.
Use it if
- You maintain older CommonJS tooling that needs the nearest package.json path from an arbitrary nested directory
- You need the same upward search in promise, Node-style callback, and synchronous code
- You need to cap how many parent directories the lookup may inspect
- Node 8 compatibility matters more than modern module syntax or bundled TypeScript declarations
- You are starting a current ESM project: find-pkg 2.0.0 is a CommonJS package last published in 2018, while find-up 8 offers a maintained ESM API for upward searches
- You need the package directory rather than the manifest filename: pkg-dir returns that directly and avoids a path.dirname step
- You need to locate filenames other than package.json or use multiple candidate names: the README explicitly says this package searches only for package.json and does not support globs
- You expect TypeScript declarations: the published 2.0.0 metadata has no types entry, so strict projects must add a local declaration or accept an untyped import
- You want an actively maintained direct dependency: the repository was last pushed in March 2018 and its generated README documents the same 2018 release-era API
Setup reality
Installation is only npm install find-pkg, with no native compilation, configuration file, credentials, or peer dependency. The compatibility cost is age and module shape. Version 2.0.0 declares Node 8 or newer, exports CommonJS from index.js, and does not ship a TypeScript declaration. In CommonJS, require('find-pkg') is the documented path; in ESM, use createRequire or test your runtime's CommonJS default-import interop instead of assuming named exports. The async function resolves to an absolute package.json path or undefined when no manifest exists, so a missing result is normal and must be handled before readFile. It does not parse the manifest. Reading and JSON.parse remain your responsibility, including malformed JSON errors. The start argument is treated as a directory and defaults to the current working directory; when processing a source filename, pass path.dirname(filename). A second numeric argument limits the number of levels searched, but that overload is documented by tests rather than the README. The synchronous form blocks filesystem work and belongs in startup or CLI code, not a request hot path. Symlinks, permission errors, and unexpected parent manifests still deserve tests in your deployment layout.
Patterns
Find the nearest package.jsonfind-nearest-manifest
const findPkg = require('find-pkg');
const manifestPath = await findPkg(process.cwd());
if (!manifestPath) throw new Error('No package.json found');
console.log(manifestPath);The result is an absolute filename, or undefined when the search reaches the root without finding a manifest.
Start from the current working directoryuse-default-directory
const findPkg = require('find-pkg');
const manifestPath = await findPkg();Omitting the start argument uses process.cwd(), which may differ from the directory containing the current module.
Search from a source file's directoryfind-from-file
const path = require('path');
const findPkg = require('find-pkg');
const sourceFile = '/workspace/app/src/index.js';
const manifestPath = await findPkg(path.dirname(sourceFile));Pass a directory. Converting a filename with path.dirname avoids treating the filename itself as a search directory.
Find and parse the nearest manifestread-package-json
const fs = require('fs/promises');
const findPkg = require('find-pkg');
const manifestPath = await findPkg();
if (!manifestPath) return null;
const pkg = JSON.parse(await fs.readFile(manifestPath, 'utf8'));
console.log(pkg.name, pkg.version);find-pkg returns a path only. File access and JSON parsing errors are yours to catch or propagate.
Derive the package root directoryget-package-directory
const path = require('path');
const findPkg = require('find-pkg');
const manifestPath = await findPkg('/workspace/app/src');
const packageDir = manifestPath ? path.dirname(manifestPath) : undefined;Use pkg-dir instead if the directory is the only result you ever need.
Stop after a fixed number of parent levelslimit-search-depth
const findPkg = require('find-pkg');
const manifestPath = await findPkg('/workspace/app/src/deep', 3);The numeric second argument is covered by the project's tests but omitted from its main README examples.
Use the Node-style callback APIuse-callback
const findPkg = require('find-pkg');
findPkg('/workspace/app/src', (error, manifestPath) => {
if (error) throw error;
if (!manifestPath) return console.log('not inside a package');
console.log(manifestPath);
});A missing package is reported as an undefined path, not as an error.
Find a manifest synchronouslyuse-sync
const findPkg = require('find-pkg');
const manifestPath = findPkg.sync(__dirname);
if (!manifestPath) throw new Error('No package.json found');The sync form blocks filesystem work; reserve it for CLI startup, configuration loading, or build scripts.
Load metadata for the containing packageload-own-metadata
const fs = require('fs');
const findPkg = require('find-pkg');
const manifestPath = findPkg.sync(__dirname);
const metadata = manifestPath
? JSON.parse(fs.readFileSync(manifestPath, 'utf8'))
: { name: 'unknown', version: 'unknown' };Starting at __dirname follows the installed module location; starting at process.cwd() follows the caller's launch directory.
Treat no manifest as a normal branchhandle-missing-manifest
const findPkg = require('find-pkg');
async function packageContext(start) {
const manifestPath = await findPkg(start);
return manifestPath ? { manifestPath } : { manifestPath: null };
}Do not pass an undefined result straight to readFile; the library intentionally resolves rather than rejects on a clean miss.
Load the CommonJS package from ESMimport-from-esm
import { createRequire } from 'node:module';
const require = createRequire(import.meta.url);
const findPkg = require('find-pkg');
const manifestPath = await findPkg(new URL('.', import.meta.url).pathname);Version 2.0.0 is CommonJS and has no exports map; createRequire avoids depending on default-import interop details.
Declare the untyped module locallyadd-typescript-shim
// types/find-pkg.d.ts
declare module 'find-pkg' {
type Callback = (error: Error | null, path?: string) => void;
function findPkg(start?: string, limit?: number): Promise<string | undefined>;
function findPkg(start: string, callback: Callback): void;
namespace findPkg {
function sync(start?: string, limit?: number): string | undefined;
}
export = findPkg;
}The package does not publish declarations. Keep a local shim small and validate overloads against the installed version.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| find-up | npm | You want a maintained ESM utility that can search for arbitrary names or use a matcher |
| pkg-dir | npm | You want the nearest package root directory rather than its package.json path |
| find-package-json | npm | You want to iterate through multiple package.json files while walking upward |