{"id":10750,"library":"dir-compare","title":"Node.js Directory Comparison Utility","description":"dir-compare is a Node.js library for comparing the contents and structure of two directories. It provides both synchronous (`compareSync`) and asynchronous (`compare`) comparison methods, supporting various strategies like size, content, and date comparison, along with advanced filtering options including glob patterns and `.gitignore` rules. The current stable version is 5.0.0, with regular updates addressing features, performance, and bug fixes. Key differentiators include its TypeScript support (since v4.0.0), significant performance improvements for large directory structures (e.g., 3x reduced heap usage and 2x faster content comparison since v4.0.0), and flexible extension points for custom comparators and result builders. The command-line interface (CLI) was moved to a separate package, `dir-compare-cli`, in v3.0.0.","status":"active","version":"5.0.0","language":"javascript","source_language":"en","source_url":"https://github.com/gliviu/dir-compare","tags":["javascript","compare","directory","folder","typescript"],"install":[{"cmd":"npm install dir-compare","lang":"bash","label":"npm"},{"cmd":"yarn add dir-compare","lang":"bash","label":"yarn"},{"cmd":"pnpm add dir-compare","lang":"bash","label":"pnpm"}],"dependencies":[{"reason":"Used internally for glob pattern filtering (includeFilter, excludeFilter).","package":"minimatch","optional":false}],"imports":[{"note":"Asynchronous comparison function. Prefer named imports with TypeScript or modern JavaScript. CommonJS `require` works but requires accessing `dircompare.compare`.","wrong":"const dircompare = require('dir-compare'); dircompare.compare(...);","symbol":"compare","correct":"import { compare } from 'dir-compare';"},{"note":"Synchronous comparison function. Named imports are recommended, especially with TypeScript since v4.0.0.","wrong":"const dircompare = require('dir-compare'); dircompare.compareSync(...);","symbol":"compareSync","correct":"import { compareSync } from 'dir-compare';"},{"note":"TypeScript interface for comparison options. Essential when writing strongly-typed comparison logic.","symbol":"Options","correct":"import { Options } from 'dir-compare';"},{"note":"TypeScript interface for the comparison result object. Provides strong typing for accessing comparison statistics and the diffSet.","symbol":"Result","correct":"import { Result } from 'dir-compare';"}],"quickstart":{"code":"import { compare, compareSync, Options, Result } from 'dir-compare';\nimport * as path from 'path';\nimport * * as fs from 'fs';\n\n// Create dummy directories and files for demonstration\nconst dir1 = path.join(__dirname, 'test-dir1');\nconst dir2 = path.join(__dirname, 'test-dir2');\n\nfs.mkdirSync(dir1, { recursive: true });\nfs.mkdirSync(dir2, { recursive: true });\nfs.writeFileSync(path.join(dir1, 'fileA.txt'), 'Content A');\nfs.writeFileSync(path.join(dir1, 'fileB.txt'), 'Content B');\nfs.writeFileSync(path.join(dir2, 'fileA.txt'), 'Content A');\nfs.writeFileSync(path.join(dir2, 'fileC.txt'), 'Content C');\n\nconst options: Options = { compareSize: true, compareContent: true, excludeFilter: 'fileB.txt' };\n\nconsole.log('--- Synchronous Comparison ---');\ntry {\n  const resSync: Result = compareSync(dir1, dir2, options);\n  console.log('Directories are %s', resSync.same ? 'identical' : 'different');\n  console.log('Equal entries: %s, Distinct entries: %s, Left only: %s, Right only: %s', \n    resSync.equal, resSync.distinct, resSync.left, resSync.right);\n  resSync.diffSet.forEach(dif => {\n    console.log(`- Path: ${dif.relativePath}, Name1: ${dif.name1}, Name2: ${dif.name2}, State: ${dif.state}`);\n  });\n} catch (error) {\n  console.error('Synchronous comparison failed:', error);\n}\n\nconsole.log('\\n--- Asynchronous Comparison ---');\ncompare(dir1, dir2, options)\n  .then(resAsync => {\n    console.log('Directories are %s', resAsync.same ? 'identical' : 'different');\n    console.log('Equal entries: %s, Distinct entries: %s, Left only: %s, Right only: %s', \n      resAsync.equal, resAsync.distinct, resAsync.left, resAsync.right);\n    resAsync.diffSet.forEach(dif => {\n      console.log(`- Path: ${dif.relativePath}, Name1: ${dif.name1}, Name2: ${dif.name2}, State: ${dif.state}`);\n    });\n  })\n  .catch(error => console.error('Asynchronous comparison failed:', error))\n  .finally(() => {\n    // Cleanup dummy directories\n    fs.rmSync(dir1, { recursive: true, force: true });\n    fs.rmSync(dir2, { recursive: true, force: true });\n  });\n","lang":"typescript","description":"This quickstart demonstrates both synchronous and asynchronous directory comparison using `dir-compare`. It creates temporary directories, populates them with files, and then compares them using options for size and content, while excluding a specific file. It logs the comparison summary and detailed differences."},"warnings":[{"fix":"Carefully test existing comparison logic if `skipSubdirs` is used and adjust expectations or options if necessary.","message":"The `skipSubdirs` option now behaves slightly differently, potentially affecting how comparisons handle subdirectories. Review issue #77 for specifics.","severity":"breaking","affected_versions":">=5.0.0"},{"fix":"If file name comparison is critical when comparing two specific files, implement a separate name check or ensure the files are placed within temporary directories for a full directory comparison.","message":"When using `dir-compare` to compare two individual files (not directories), the names of the files are now ignored in the comparison. This primarily affects scenarios where `dir-compare` was used for direct file-to-file comparison and relied on name matching.","severity":"breaking","affected_versions":">=4.0.0"},{"fix":"For TypeScript projects, update imports to use named imports (e.g., `import { compare, Options } from 'dir-compare';`). JavaScript projects should continue to function but may benefit from type definitions.","message":"The project was switched to TypeScript in v4.0.0. While existing JavaScript usage generally remains compatible, direct imports for types (`Options`, `Result`) are now available, and the internal structure is type-checked. This might implicitly affect type inference in certain editors for existing JavaScript projects.","severity":"breaking","affected_versions":">=4.0.0"},{"fix":"If you rely on the `dir-compare` CLI, install `dir-compare-cli` separately (`npm install -g dir-compare-cli`) and use its commands instead.","message":"The command-line interface (CLI) utility was extracted into a separate package, `dir-compare-cli`. The `dir-compare` package now only provides the library API.","severity":"breaking","affected_versions":">=3.0.0"},{"fix":"Review code that processes `diffSet` entries to leverage the new `origin` field for more precise handling of left-only vs. right-only differences, if needed.","message":"The `origin` field was added to the `Entry` interface (within `diffSet`) to distinguish whether an entry originated from the left or right directory. This provides more granular information but requires updating code that processed `diffSet` entries if `origin` is now relevant.","severity":"gotcha","affected_versions":">=4.1.0"},{"fix":"Consider refactoring custom filtering logic to utilize the new `glob filter` and `.gitignore` implementation features provided by the library, which can improve maintainability and robustness.","message":"Since v4.1.0, the library offers enhanced glob filter capabilities and the ability to implement `.gitignore` rules. If custom filtering logic was previously implemented manually, these new features can simplify the code.","severity":"gotcha","affected_versions":">=4.1.0"}],"env_vars":null,"last_verified":"2026-04-19T00:00:00.000Z","next_check":"2026-07-18T00:00:00.000Z","problems":[{"fix":"For CommonJS, try `const { compare, compareSync } = require('dir-compare');` or for modern ES Modules: `import { compare, compareSync } from 'dir-compare';`. Ensure you are calling `compare` (async) with `.then()`/`await` or `compareSync` (sync) appropriately.","cause":"Attempting to call `dircompare.compare` directly after a CommonJS `require('dir-compare')` on versions 4.0.0+ when the library might expose named exports more prominently, or if trying to call an async function synchronously.","error":"TypeError: dircompare.compare is not a function"},{"fix":"Ensure the user running the Node.js application has read and execute permissions on all directories and files within the comparison paths. Alternatively, `dir-compare` added support for handling permission denied errors in v3.2.0; investigate options for graceful error handling within the comparison process if appropriate.","cause":"The Node.js process does not have sufficient read permissions for one or both of the directories or files being compared.","error":"Error: EACCES: permission denied, open 'path/to/file'"},{"fix":"Wrap the comparison call in a `try...catch` block (for sync) or add a `.catch()` handler (for async) to properly handle potential errors during the comparison process. Also, verify that the input paths are valid and accessible.","cause":"This usually happens if the `compare` or `compareSync` function failed to execute or returned an unexpected value (e.g., `null` or `undefined`) instead of a `Result` object, and subsequent code tries to access properties like `diffSet`.","error":"TypeError: Cannot read properties of undefined (reading 'diffSet')"}],"ecosystem":"npm"}