Skip to content

mdjastrzebski/fs-fingerprint

Repository files navigation

FS Fingerprint 🫆

Generate unique fingerprint hashes from filesystem state and other inputs: text, JSON, envs.

What is FS Fingerprint?

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. ⚡

Features

  • Reliable and fast change detection (100% code coverage, benchmarked)
  • Highly customizable: files/ignores glob patterns, additional inputs, hashing algorithms, .gitignore support
  • Elegant and simple TypeScript API, both sync and async
  • Tiny size (~30 KB unpacked) with minimal dependencies (tinyglobby)

Quick Start

  1. Install:
    npm install fs-fingerprint
    (or yarn/pnpm/bun add fs-fingerprint)
  2. Usage:
import { calculateFingerprint } from "fs-fingerprint";

const { hash } = await calculateFingerprint("/project/path", {
  files: ["ios/", "package.json"],
  ignores: ["ios/build/"],
});

Core API

calculateFingerprint

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.

Return Value

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.

Examples

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 });

Design Considerations

  1. 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.

  2. 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.

  3. Minimal Dependencies: Avoid adding 3rd party deps unless highly beneficial.

  4. Benchmark Everything: Do not make assumptions about what is fast and what is not, always benchmark any introduced code changes.

Contributing

PRs welcome! Keep it awesome.

License

MIT 💝


Made with 💻 and ☕️ by MDJ

About

Node library for generating fingerprints for files and directories.

Resources

License

Stars

Watchers

Forks

Packages

No packages published