Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

folder-hash

Package Overview
Dependencies
Maintainers
1
Versions
24
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

folder-hash - npm Package Compare versions

Comparing version 3.3.3 to 4.0.0

99

cli.js

@@ -5,72 +5,73 @@ const fs = require('graceful-fs');

function program(cliArgs) {
let args;
try {
args = parseArgs(cliArgs);
} catch (ex) {
error(ex);
}
let args;
try {
args = parseArgs(cliArgs);
} catch (ex) {
error(ex);
}
if (args.help) {
printHelp();
process.exit(0);
}
if (args.help) {
printHelp();
process.exit(0);
}
let config;
if (args.config) {
try {
config = JSON.parse(fs.readFileSync(args.config, { encoding: 'utf-8' }));
} catch (err) {
console.error('Could not parse configuration from file ' + args.config);
console.error('Maybe try a JSON config like this instead?\n');
console.error(JSON.stringify(lib.defaults, undefined, 2));
process.exit(1);
}
let config;
if (args.config) {
try {
config = JSON.parse(fs.readFileSync(args.config, { encoding: 'utf-8' }));
} catch (err) {
console.error('Could not parse configuration from file ' + args.config);
console.error('Maybe try a JSON config like this instead?\n');
console.error(JSON.stringify(lib.defaults, undefined, 2));
process.exit(1);
}
}
return lib.hashElement(args.src || process.cwd(), config)
.then(result => console.log(result.toString()))
.catch(error);
return lib
.hashElement(args.src || process.cwd(), config)
.then(result => console.log(result.toString()))
.catch(error);
}
function parseArgs(args) {
let help, config, src
let help, config, src;
for (let index = 0; index < args.length; index++) {
switch (args[index]) {
case '-h':
case '--help':
help = true;
break;
for (let index = 0; index < args.length; index++) {
switch (args[index]) {
case '-h':
case '--help':
help = true;
break;
case '-c':
case '--config':
if (!args[++index]) {
throw new Error(`Need to supply a JSON config file after "${args[index]}"`);
}
config = args[index];
break;
case '-c':
case '--config':
if (!args[++index]) {
throw new Error(`Need to supply a JSON config file after "${args[index]}"`);
}
config = args[index];
break;
default:
if (!src) {
src = args[index];
} else {
console.log(`Ignoring param "${args[index]}"`);
}
break;
default:
if (!src) {
src = args[index];
} else {
console.log(`Ignoring param "${args[index]}"`);
}
break;
}
}
return { help, config, src };
return { help, config, src };
}
function error(err) {
console.error('ERROR:', ex.message || ex.name || ex);
process.exit(1);
console.error('ERROR:', ex.message || ex.name || ex);
process.exit(1);
}
function printHelp() {
console.log('Use folder-hash on cli like this:');
console.log(' folder-hash [--config <json-file>] <file-or-folder>');
console.log('Use folder-hash on cli like this:');
console.log(' folder-hash [--config <json-file>] <file-or-folder>');
}
module.exports = program;
const crypto = require('crypto'),
debug = require('debug'),
minimatch = require('minimatch'),
path = require('path');
debug = require('debug'),
minimatch = require('minimatch'),
path = require('path');
const defaultOptions = {
algo: 'sha1', // see crypto.getHashes() for options
encoding: 'base64', // 'base64', 'hex' or 'binary'
files: {
exclude: [],
include: [],
matchBasename: true,
matchPath: false,
ignoreBasename: false,
ignoreRootName: false
},
folders: {
exclude: [],
include: [],
matchBasename: true,
matchPath: false,
ignoreBasename: false,
ignoreRootName: false
}
algo: 'sha1', // see crypto.getHashes() for options
encoding: 'base64', // 'base64', 'hex' or 'binary'
files: {
exclude: [],
include: [],
matchBasename: true,
matchPath: false,
ignoreBasename: false,
ignoreRootName: false,
},
folders: {
exclude: [],
include: [],
matchBasename: true,
matchPath: false,
ignoreBasename: false,
ignoreRootName: false,
},
symbolicLinks: {
include: true,
ignoreBasename: false,
ignoreTargetPath: true,
ignoreTargetContent: false,
ignoreTargetContentAfterError: false,
},
};

@@ -29,281 +36,353 @@

const log = {
match: debug('fhash:match'),
params: (params) => {
debug('fhash:parameters')(params);
return params;
}
match: debug('fhash:match'),
params: params => {
debug('fhash:parameters')(params);
return params;
},
err: debug('fhash:err'),
symlink: debug('fhash:symlink'),
};
function prep(fs, Promise) {
function hashElement(name, dir, options, callback) {
callback = arguments[arguments.length - 1];
function prep(fs) {
function hashElement(name, dir, options, callback) {
callback = arguments[arguments.length - 1];
return parseParameters(arguments)
.then(({ basename, dir, options }) => {
// this is only used for the root level
options.skipMatching = true;
return hashElementPromise(basename, dir, options, true);
})
.then(result => {
if (isFunction(callback)) {
return callback(undefined, result);
} else {
return result;
}
})
.catch(reason => {
if (isFunction(callback)) {
return callback(reason);
} else {
throw reason;
}
});
}
return parseParameters(arguments)
.then(({ basename, dir, options }) => {
// this is only used for the root level
options.skipMatching = true;
return fs.promises
.lstat(path.join(dir, basename))
.then(stats => {
stats.name = basename;
return stats;
})
.then(stats => hashElementPromise(stats, dir, options, true));
})
.then(result => {
if (isFunction(callback)) {
return callback(undefined, result);
} else {
return result;
}
})
.catch(reason => {
log.err('Fatal error:', reason);
if (isFunction(callback)) {
return callback(reason);
} else {
throw reason;
}
});
}
function hashElementPromise(basename, dirname, options, isRootElement = false) {
return stat(path.join(dirname, basename)).then(stats => {
if (stats.isDirectory()) {
return hashFolderPromise(basename, dirname, options, isRootElement);
} else if (stats.isFile()) {
return hashFilePromise(basename, dirname, options, isRootElement);
} else {
return {
name: basename,
hash: 'unknown element type'
};
}
});
/**
* @param {fs.Stats} stats folder element, can also be of type fs.Dirent
* @param {string} dirname
* @param {Options} options
* @param {boolean} isRootElement
*/
function hashElementPromise(stats, dirname, options, isRootElement = false) {
const name = stats.name;
if (stats.isDirectory()) {
return hashFolderPromise(name, dirname, options, isRootElement);
} else if (stats.isFile()) {
return hashFilePromise(name, dirname, options, isRootElement);
} else if (stats.isSymbolicLink()) {
return hashSymLinkPromise(name, dirname, options, isRootElement);
} else {
log.err('hashElementPromise cannot handle ', stats);
return { name, hash: 'Error: unknown element type' };
}
}
function stat(filepath) {
return new Promise((resolve, reject) => {
fs.stat(filepath, (err, stats) => {
if (err) {
return reject(err);
} else {
return resolve(stats);
}
});
});
function hashFolderPromise(name, dir, options, isRootElement = false) {
const folderPath = path.join(dir, name);
let ignoreBasenameOnce = options.ignoreBasenameOnce;
delete options.ignoreBasenameOnce;
if (options.skipMatching) {
// this is currently only used for the root folder
log.match(`skipped '${folderPath}'`);
delete options.skipMatching;
} else if (ignore(name, folderPath, options.folders)) {
return undefined;
}
function hashFolderPromise(name, dir, options, isRootElement = false) {
const folderPath = path.join(dir, name);
return fs.promises.readdir(folderPath, { withFileTypes: true }).then(files => {
const children = files.sort().map(child => {
return hashElementPromise(child, folderPath, options);
});
if (options.skipMatching) {
// this is currently only used for the root folder
log.match(`skipped '${folderPath}'`);
delete options.skipMatching;
} else if (ignore(name, folderPath, options.folders)) {
return undefined;
}
return Promise.all(children).then(children => {
if (ignoreBasenameOnce) options.ignoreBasenameOnce = true;
const hash = new HashedFolder(name, children.filter(notUndefined), options, isRootElement);
return hash;
});
});
}
return readdir(folderPath).then(files => {
const children = files.map(child => {
return hashElementPromise(child, folderPath, options);
});
function hashFilePromise(name, dir, options, isRootElement = false) {
const filePath = path.join(dir, name);
return Promise.all(children).then(children => {
const hash = new HashedFolder(name, children.filter(notUndefined), options, isRootElement);
return hash;
});
});
if (options.skipMatching) {
// this is currently only used for the root folder
log.match(`skipped '${filePath}'`);
delete options.skipMatching;
} else if (ignore(name, filePath, options.files)) {
return undefined;
}
function readdir(folderPath) {
return new Promise((resolve, reject) => {
fs.readdir(folderPath, (err, files) => {
if (err) {
console.error(err);
return reject(err);
} else {
return resolve(files.sort());
}
});
});
}
return new Promise((resolve, reject) => {
try {
const hash = crypto.createHash(options.algo);
if (
options.files.ignoreBasename ||
options.ignoreBasenameOnce ||
(isRootElement && options.files.ignoreRootName)
) {
delete options.ignoreBasenameOnce;
log.match(`omitted name of ${filePath} from hash`);
} else {
hash.update(name);
}
function hashFilePromise(name, dir, options, isRootElement = false) {
const filePath = path.join(dir, name);
const f = fs.createReadStream(filePath);
f.pipe(hash, { end: false });
if (options.skipMatching) {
// this is currently only used for the root folder
log.match(`skipped '${filePath}'`);
delete options.skipMatching;
} else if (ignore(name, filePath, options.files)) {
return undefined;
}
f.on('end', () => {
const hashedFile = new HashedFile(name, hash, options.encoding);
return resolve(hashedFile);
});
} catch (ex) {
return reject(ex);
}
});
}
return new Promise((resolve, reject) => {
try {
const hash = crypto.createHash(options.algo);
if (options.files.ignoreBasename ||
(isRootElement && options.files.ignoreRootName))
{
log.match(`omitted name of ${filePath} from hash`)
} else {
hash.write(name);
}
async function hashSymLinkPromise(name, dir, options, isRootElement = false) {
const target = await fs.promises.readlink(path.join(dir, name));
log.symlink(`handling symbolic link ${name} -> ${target}`);
if (options.symbolicLinks.include) {
if (options.symbolicLinks.ignoreTargetContent) {
return symLinkIgnoreTargetContent(name, target, options, isRootElement);
} else {
return symLinkResolve(name, dir, target, options, isRootElement);
}
} else {
log.symlink('skipping symbolic link');
return Promise.resolve(undefined);
}
}
const f = fs.createReadStream(filePath);
f.pipe(hash, { end: false });
function symLinkIgnoreTargetContent(name, target, options, isRootElement) {
delete options.skipMatching; // only used for the root level
log.symlink('ignoring symbolic link target content');
const hash = crypto.createHash(options.algo);
if (!options.symbolicLinks.ignoreBasename && !(isRootElement && options.files.ignoreRootName)) {
log.symlink('hash basename');
hash.update(name);
}
if (!options.symbolicLinks.ignoreTargetPath) {
log.symlink('hash targetpath');
hash.update(target);
}
return Promise.resolve(new HashedFile(name, hash, options.encoding));
}
f.on('end', () => {
const hashedFile = new HashedFile(name, hash, options.encoding);
return resolve(hashedFile);
});
} catch (ex) {
return reject(ex);
}
});
async function symLinkResolve(name, dir, target, options, isRootElement) {
delete options.skipMatching; // only used for the root level
if (options.symbolicLinks.ignoreBasename) {
options.ignoreBasenameOnce = true;
}
function ignore(name, path, rules) {
if (rules.exclude) {
if (rules.matchBasename && rules.exclude(name)) {
log.match(`exclude basename '${path}'`);
return true;
} else if (rules.matchPath && rules.exclude(path)) {
log.match(`exclude path '${path}'`);
return true;
}
} else if (rules.include) {
if (rules.matchBasename && rules.include(name)) {
log.match(`include basename '${path}'`);
return false;
} else if (rules.matchPath && rules.include(path)) {
log.match(`include path '${path}'`);
return false;
} else {
return true;
}
try {
const stats = await fs.promises.stat(path.join(dir, name));
stats.name = name;
const temp = await hashElementPromise(stats, dir, options, isRootElement);
if (!options.symbolicLinks.ignoreTargetPath) {
const hash = crypto.createHash(options.algo);
hash.update(temp.hash);
log.symlink('hash targetpath');
hash.update(target);
temp.hash = hash.digest(options.encoding);
}
return temp;
} catch (err) {
if (options.symbolicLinks.ignoreTargetContentAfterError) {
log.symlink(`Ignoring error "${err.code}" when hashing symbolic link ${name}`, err);
const hash = crypto.createHash(options.algo);
if (
!options.symbolicLinks.ignoreBasename &&
!(isRootElement && options.files.ignoreRootName)
) {
hash.update(name);
}
if (!options.symbolicLinks.ignoreTargetPath) {
hash.update(target);
}
return new HashedFile(name, hash, options.encoding);
} else {
log.symlink(`Error "${err.code}": When hashing symbolic link ${name}`, err);
throw err;
}
}
}
log.match(`unmatched '${path}'`);
function ignore(name, path, rules) {
if (rules.exclude) {
if (rules.matchBasename && rules.exclude(name)) {
log.match(`exclude basename '${name}'`);
return true;
} else if (rules.matchPath && rules.exclude(path)) {
log.match(`exclude path '${path}'`);
return true;
}
}
if (rules.include) {
if (rules.matchBasename && rules.include(name)) {
log.match(`include basename '${name}'`);
return false;
} else if (rules.matchPath && rules.include(path)) {
log.match(`include path '${path}'`);
return false;
} else {
log.match(`include rule failed for path '${path}'`);
return true;
}
}
const HashedFolder = function HashedFolder(name, children, options, isRootElement = false) {
this.name = name;
this.children = children;
log.match(`Will not ignore unmatched '${path}'`);
return false;
}
const hash = crypto.createHash(options.algo);
if (options.folders.ignoreBasename ||
(isRootElement && options.folders.ignoreRootName))
{
log.match(`omitted name of folder ${name} from hash`)
} else {
hash.write(name);
}
children.forEach(child => {
if (child.hash) {
hash.write(child.hash);
}
});
return hashElement;
}
this.hash = hash.digest(options.encoding);
};
function parseParameters(args) {
let basename = args[0],
dir = args[1],
options_ = args[2];
HashedFolder.prototype.toString = function (padding = '') {
const first = `${padding}{ name: '${this.name}', hash: '${this.hash}',\n`;
padding += ' ';
if (!isString(basename)) {
return Promise.reject(new TypeError('First argument must be a string'));
}
return `${first}${padding}children: ${this.childrenToString(padding)}}`;
};
if (!isString(dir)) {
dir = path.dirname(basename);
basename = path.basename(basename);
options_ = args[1];
}
HashedFolder.prototype.childrenToString = function (padding = '') {
if (this.children.length === 0) {
return '[]';
} else {
const nextPadding = padding + ' ';
const children = this.children
.map(child => child.toString(nextPadding))
.join('\n');
return `[\n${children}\n${padding}]`;
}
};
// parse options (fallback default options)
if (!isObject(options_)) options_ = {};
const options = {
algo: options_.algo || defaultOptions.algo,
encoding: options_.encoding || defaultOptions.encoding,
files: Object.assign({}, defaultOptions.files, options_.files),
folders: Object.assign({}, defaultOptions.folders, options_.folders),
match: Object.assign({}, defaultOptions.match, options_.match),
symbolicLinks: Object.assign({}, defaultOptions.symbolicLinks, options_.symbolicLinks),
};
const HashedFile = function HashedFile(name, hash, encoding) {
this.name = name;
this.hash = hash.digest(encoding);
};
// transform match globs to Regex
options.files.exclude = reduceGlobPatterns(options.files.exclude);
options.files.include = reduceGlobPatterns(options.files.include);
options.folders.exclude = reduceGlobPatterns(options.folders.exclude);
options.folders.include = reduceGlobPatterns(options.folders.include);
HashedFile.prototype.toString = function (padding = '') {
return padding + '{ name: \'' + this.name + '\', hash: \'' + this.hash + '\' }';
};
return hashElement;
return Promise.resolve(log.params({ basename, dir, options }));
}
function parseParameters(args) {
let basename = args[0],
dir = args[1],
options_ = args[2];
const HashedFolder = function HashedFolder(name, children, options, isRootElement = false) {
this.name = name;
this.children = children;
if (!isString(basename)) {
return Promise.reject(new TypeError('First argument must be a string'));
const hash = crypto.createHash(options.algo);
if (
options.folders.ignoreBasename ||
options.ignoreBasenameOnce ||
(isRootElement && options.folders.ignoreRootName)
) {
delete options.ignoreBasenameOnce;
log.match(`omitted name of folder ${name} from hash`);
} else {
hash.update(name);
}
children.forEach(child => {
if (child.hash) {
hash.update(child.hash);
}
});
if (!isString(dir)) {
dir = path.dirname(basename);
basename = path.basename(basename);
options_ = args[1];
}
this.hash = hash.digest(options.encoding);
};
// parse options (fallback default options)
if (!isObject(options_)) options_ = {};
const options = {
algo: options_.algo || defaultOptions.algo,
encoding: options_.encoding || defaultOptions.encoding,
files: Object.assign({}, defaultOptions.files, options_.files),
folders: Object.assign({}, defaultOptions.folders, options_.folders),
match: Object.assign({}, defaultOptions.match, options_.match)
};
HashedFolder.prototype.toString = function (padding = '') {
const first = `${padding}{ name: '${this.name}', hash: '${this.hash}',\n`;
padding += ' ';
// transform match globs to Regex
options.files.exclude = reduceGlobPatterns(options.files.exclude);
options.files.include = reduceGlobPatterns(options.files.include);
options.folders.exclude = reduceGlobPatterns(options.folders.exclude);
options.folders.include = reduceGlobPatterns(options.folders.include);
return `${first}${padding}children: ${this.childrenToString(padding)}}`;
};
return Promise.resolve(log.params({ basename, dir, options }));
}
HashedFolder.prototype.childrenToString = function (padding = '') {
if (this.children.length === 0) {
return '[]';
} else {
const nextPadding = padding + ' ';
const children = this.children.map(child => child.toString(nextPadding)).join('\n');
return `[\n${children}\n${padding}]`;
}
};
const HashedFile = function HashedFile(name, hash, encoding) {
this.name = name;
this.hash = hash.digest(encoding);
};
HashedFile.prototype.toString = function (padding = '') {
return padding + "{ name: '" + this.name + "', hash: '" + this.hash + "' }";
};
function isFunction(any) {
return typeof any === 'function';
return typeof any === 'function';
}
function isString(str) {
return typeof str === 'string' || str instanceof String;
return typeof str === 'string' || str instanceof String;
}
function isObject(obj) {
return obj !== null && typeof obj === 'object';
return obj !== null && typeof obj === 'object';
}
function notUndefined(obj) {
return typeof obj !== 'undefined';
return typeof obj !== 'undefined';
}
function reduceGlobPatterns(globs) {
if (isFunction(globs)) {
return globs;
}
else if (!globs || !Array.isArray(globs) || globs.length === 0) {
return undefined;
} else {
// combine globs into one single RegEx
const regex = new RegExp(globs.reduce((acc, exclude) => {
return acc + '|' + minimatch.makeRe(exclude).source;
}, '').substr(1));
return param => regex.test(param);
}
if (isFunction(globs)) {
return globs;
} else if (!globs || !Array.isArray(globs) || globs.length === 0) {
return undefined;
} else {
// combine globs into one single RegEx
const regex = new RegExp(
globs
.reduce((acc, exclude) => {
return acc + '|' + minimatch.makeRe(exclude).source;
}, '')
.substr(1),
);
return param => regex.test(param);
}
}
module.exports = {
defaults: defaultOptions,
hashElement: prep(require("graceful-fs"), Promise),
// exposed for testing
prep: prep,
parseParameters: parseParameters
defaults: defaultOptions,
hashElement: prep(require('graceful-fs')),
// exposed for testing
prep,
parseParameters,
};
{
"name": "folder-hash",
"version": "3.3.3",
"version": "4.0.0",
"description": "Create a hash checksum over a folder and its content - its children and their content",

@@ -13,2 +13,3 @@ "main": "index.js",

"cover": "nyc mocha test",
"format": "prettier --write *.js examples/ test/",
"doc": "./node_modules/.bin/jsdoc index.js -R README.md -d doc"

@@ -45,11 +46,13 @@ },

"chai-as-promised": "^7.1.1",
"clone": "^2.1.2",
"ignore": "^5.1.2",
"jsdoc": "3.6.4",
"jsdoc": "3.6.6",
"memfs": "^3.0.4",
"mocha": "^8.0.1",
"nyc": "^15.0.0"
"nyc": "^15.0.0",
"prettier": "~2.1.1"
},
"engines": {
"node": ">=6.0.0"
"node": ">=10.10.0"
}
}

@@ -5,11 +5,12 @@ Create a hash checksum over a folder or a file.

Each file returns a name and a hash, and each folder returns additionally an array of children (file or folder elements).
Each file returns a name and a hash, and each folder returns additionally an array of children (file or folder elements).
## Usage
First, install folder-hash with `npm install --save folder-hash` or `yarn add folder-hash`.
## Usage
First, install folder-hash with `npm install --save folder-hash` or `yarn add folder-hash`.
### Simple example
To see differences to the last version of this package, I would create hashes over all *.js* and *.json* files. But ignore everything inside folders starting wiht a dot, and also from the folders *node_modules*, *test_coverage*. The structure of the options object is documented <a href="#options">on this page.</a>
This example is also stored in [./examples/readme-example1.js](/examples/readme-example1.js).
To see differences to the last version of this package, I would create hashes over all _.js_ and _.json_ files. But ignore everything inside folders starting with a dot, and also from the folders _node_modules_, _test_coverage_. The structure of the options object is documented <a href="#options">below.</a>
This example is also stored in [./examples/readme-example1.js](/examples/readme-example1.js).

@@ -20,4 +21,4 @@ ```js

const options = {
folders: { exclude: ['.*', 'node_modules', 'test_coverage'] },
files: { include: ['*.js', '*.json'] }
folders: { exclude: ['.*', 'node_modules', 'test_coverage'] },
files: { include: ['*.js', '*.json'] },
};

@@ -27,11 +28,12 @@

hashElement('.', options)
.then(hash => {
console.log(hash.toString());
})
.catch(error => {
return console.error('hashing failed:', error);
});
.then(hash => {
console.log(hash.toString());
})
.catch(error => {
return console.error('hashing failed:', error);
});
```
The returned information looks for example like this:
```

@@ -57,21 +59,24 @@ Creating a hash over the current folder:

```
And the structure may be traversed to e.g. create incremental backups.
It is also possible to only match the full path and not the basename. The same configuration could look like this:
_You should be aware that *nix and Windows behave differently, so please use caution._
_You should be aware that \*nix and Windows behave differently, so please use caution._
```js
const options = {
folders: {
exclude: ['.*', '**.*', '**node_modules', '**test_coverage'],
matchBasename: false, matchPath: true
},
files: {
//include: ['**.js', '**.json' ], // Windows
include: ['*.js', '**/*.js', '*.json', '**/*.json'], // *nix
matchBasename: false, matchPath: true
}
folders: {
exclude: ['.*', '**.*', '**node_modules', '**test_coverage'],
matchBasename: false,
matchPath: true,
},
files: {
//include: ['**.js', '**.json' ], // Windows
include: ['*.js', '**/*.js', '*.json', '**/*.json'], // *nix
matchBasename: false,
matchPath: true,
},
};
```
### Parameters for the hashElement function

@@ -134,3 +139,5 @@

## Options
### Default values
```js

@@ -154,2 +161,11 @@ {

ignoreRootName: false
},
symbolicLinks: {
symbolicLinks: {
include: true,
ignoreBasename: false,
ignoreTargetPath: true,
ignoreTargetContent: false,
ignoreTargetContentAfterError: false,
}
}

@@ -220,2 +236,14 @@ }

</tr>
<tr>
<td>symLinks</td>
<td>
<span>Object</span>
</td>
<td>
&lt;optional&gt;<br>
</td>
<td colspan="2">
<a href="#symlink-options">Symlink options (see below)</a>
</td>
</tr>
</tbody>

@@ -225,2 +253,3 @@ </table>

#### Rules object properties
<table>

@@ -318,8 +347,80 @@ <thead>

### Symlink options
Configure, how symbolic links should be hashed.
To understand how the options can be combined to create a specific behavior, look into [test/symbolic-links.js](https://github.com/marc136/node-folder-hash/blob/master/test/symbolic-links.js).
<table>
<thead>
<tr>
<th>Name</th>
<th>Type</th>
<th>Default</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>include</td>
<td>
<span>bool</span>
</td>
<td>
true
</td>
<td>If false, symbolic links are not handled at all. A folder with three symbolic links inside will have no children entries.</td>
</tr>
<tr>
<td>ignoreBasename</td>
<td>
<span>bool</span>
</td>
<td>
false
</td>
<td>Set to true to calculate the hash without the basename element</td>
</tr>
<tr>
<td>ignoreTargetPath</td>
<td>
<span>bool</span>
</td>
<td>
true
</td>
<td>If false, the resolved link target is added to the hash (uses <a href="https://devdocs.io/node/fs#fs_fs_readlink_path_options_callback">fs.readlink</a>)</td>
</tr>
<tr>
<td>ignoreTargetContent</td>
<td>
<span>bool</span>
</td>
<td>
false
</td>
<td>If true, will only assess the basename and target path (as configured in the other options)</td>
</tr>
<tr>
<td>ignoreTargetContentAfterError</td>
<td>
<span>bool</span>
</td>
<td>
false
</td>
<td>If true, will ignore all errors while trying to hash symbolic links and only assess the basename and target path (as configured in other options).<br />E.g. a missing target (<i>ENOENT</i>) or access permissions (<i>EPERM</i>).</td>
</tr>
</tbody>
</table>
## Command line usage
After installing it globally via
```
$ npm install -g folder-hash
```
You can use it like this:
```

@@ -337,2 +438,3 @@ # local folder

You can also use a local version of folder-hash like this:
```

@@ -345,4 +447,7 @@ $ npx folder-hash --help

## Examples
### Other examples using promises
See file *./examples/readme-with-promises.js*
See file _./examples/readme-with-promises.js_
```js

@@ -384,4 +489,5 @@ const path = require('path');

### Other examples using error-first callbacks
See *./examples/readme-with-callbacks.js*
See _./examples/readme-with-callbacks.js_
```js

@@ -393,7 +499,7 @@ const path = require('path');

hashElement('test', path.join(__dirname, '..'), (error, hash) => {
if (error) {
return console.error('hashing failed:', error);
} else {
console.log('Result for folder "../test":', hash.toString(), '\n');
}
if (error) {
return console.error('hashing failed:', error);
} else {
console.log('Result for folder "../test":', hash.toString(), '\n');
}
});

@@ -403,8 +509,8 @@

hashElement(__dirname, (error, hash) => {
if (error) {
return console.error('hashing failed:', error);
} else {
console.log('Result for folder "' + __dirname + '":');
console.log(hash.toString(), '\n');
}
if (error) {
return console.error('hashing failed:', error);
} else {
console.log('Result for folder "' + __dirname + '":');
console.log(hash.toString(), '\n');
}
});

@@ -415,18 +521,18 @@

hashElement(__dirname, options, (error, hash) => {
if (error) {
return console.error('hashing failed:', error);
} else {
console.log('Result for folder "' + __dirname + '":');
console.log(hash.toString());
}
if (error) {
return console.error('hashing failed:', error);
} else {
console.log('Result for folder "' + __dirname + '":');
console.log(hash.toString());
}
});
```
## Behavior
## Behavior
The behavior is documented and verified in the unit tests. Execute `npm test` or `mocha test`, and have a look at the _test_ subfolder.
You can also have a look at the [CircleCI report. ![CircleCI](https://circleci.com/gh/marc136/node-folder-hash/tree/master.svg?style=svg)](https://circleci.com/gh/marc136/node-folder-hash/tree/master)
### Creating hashes over files (with default options)
### Creating hashes over files (with default options)
**The hashes are the same if:**

@@ -444,2 +550,3 @@

### Creating hashes over folders (with default options)
Content means in this case a folder's children - both the files and the subfolders with their children.

@@ -459,2 +566,3 @@

## License
MIT, see LICENSE.txt
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc