Generate unique fingerprint hashes from filesystem state and other inputs: text, JSON, envs.
A fast Node.js library to generate unique fingerprints based on:
- Files & directories in your project
- Other inputs: text content, JSON data, environment variables
Perfect for building intelligent caching solutions that detect when source files have changed. ⚡
- Reliable and fast change detection (100% code coverage, benchmarked)
- Highly customizable: files/ignores glob patterns, additional inputs, hashing algorithms,
.gitignoresupport - Elegant and simple TypeScript API, both sync and async
- Tiny size (~30 KB unpacked) with minimal dependencies (
tinyglobby)
- Install:
npm install fs-fingerprint
(oryarn/pnpm/bun add fs-fingerprint) - Usage:
import { calculateFingerprint } from "fs-fingerprint";
const { hash } = await calculateFingerprint("/project/path", {
files: ["ios/", "package.json"],
ignores: ["ios/build/"],
});async function calculateFingerprint(
basePath: string, // Base path to resolve "files" and "ignores" patterns
options?: {
files?: string[]; // Glob patterns to include (default: all)
ignores?: string[]; // Glob patterns to exclude (default: none)
contentInputs?: ContentInput[]; // Additional inputs: text, JSON, envs, etc.
hashAlgorithm?: string; // Hash algorithm (default: "sha1")
gitIgnore?: boolean; // Exclude paths ignored by Git (default: false)
},
): Promise<Fingerprint>;Generates a fingerprint hash for the filesystem state.
interface Fingerprint {
hash: string; // Overall project fingerprint hash
files: FileHash[]; // File hashes included in the fingerprint
content: ContentHash[]; // Content hashes included in the fingerprint
}Note: when using gitIgnore option, it silently ignores any git invocation errors (e.g. missing git binary, or not a git repository).
Browse the API Reference for other API.
Basic usage:
const { hash } = await calculateFingerprint("/project/path");
console.log(hash); // "abc123..."Using include/exclude patterns:
const { hash } = await calculateFingerprint("/project/path", {
files: ["src/", "package.json"],
ignores: ["**/*.test.ts", "dist"],
});Using content inputs:
const { hash } = await calculateFingerprint("/project/path", {
contentInputs: [
textContent("app-config", "debug=true"), // Text content
jsonContent("app-metadata", { version: "1.0", env: "prod" }), // JSON data
envContent("app-envs", ["BUILD_ENVIRONMENT", "FEATURE_FLAG"]), // Env variables
envContent("signing-key", ["API_KEY"], { secret: true }), // Secret env input (value not included in details)
],
});Using .gitignore file:
const { hash } = await calculateFingerprint("/project/path", {
gitIgnore: true,
});Custom hash algorithm:
const { hash } = await calculateFingerprint("/project/path", {
hashAlgorithm: "sha512",
});Synchronous call:
const { hash } = calculateFingerprintSync("/project/path", { ...options });-
Flat manifest:
The final hash is computed from a list of all files and their hashes, sorted by relative path. Renaming or moving a file changes the fingerprint, even if content is unchanged. -
File Hashing:
Each file’s hash is based only on its content (not name or path). The final hash includes both file paths and their content hashes. -
Minimal Dependencies: Avoid adding 3rd party deps unless highly beneficial.
-
Benchmark Everything: Do not make assumptions about what is fast and what is not, always benchmark any introduced code changes.
PRs welcome! Keep it awesome.
MIT 💝
Made with 💻 and ☕️ by MDJ