folder-hash
Advanced tools
Comparing version 1.1.2 to 2.0.0
// execute from the base folder | ||
// node examples\readme-with-callbacks.js | ||
var hasher = require('../index.js'); | ||
const path = require('path'); | ||
const { hashElement } = require('../index.js'); | ||
// pass element name and folder path separately | ||
hasher.hashElement('node_modules', __dirname).then(function (hash) { | ||
console.log('Result for folder "node_modules" in directory "' + __dirname + '":'); | ||
console.log(hash.toString()); | ||
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'); | ||
} | ||
}); | ||
// pass element path directly | ||
hasher.hashElement(__dirname).then(function (hash) { | ||
console.log('Result for folder "' + __dirname + '":'); | ||
console.log(hash.toString()); | ||
hashElement(__dirname, (error, hash) => { | ||
if (error) { | ||
return console.error('hashing failed:', error); | ||
} else { | ||
console.log('Result for folder "' + __dirname + '":'); | ||
console.log(hash.toString(), '\n'); | ||
} | ||
}); | ||
// pass options (example: exclude dotFiles) | ||
var options = { excludes: ['.*'], match: { basename: true, path: false } }; | ||
hasher.hashElement(__dirname, options) | ||
.then(function (hash) { | ||
console.log('Result for folder "' + __dirname + '":'); | ||
console.log(hash.toString()); | ||
}) | ||
.catch(function (error) { | ||
return console.error('hashing failed:', error); | ||
const options = { algo: 'md5', files: { exclude: ['.*'], matchBasename: true } }; | ||
hashElement(__dirname, options, (error, hash) => { | ||
if (error) { | ||
return console.error('hashing failed:', error); | ||
} else { | ||
console.log('Result for folder "' + __dirname + '":'); | ||
console.log(hash.toString()); | ||
} | ||
}); |
// execute from the base folder | ||
// node examples\readme-with-promises.js | ||
var hasher = require('../index.js'); | ||
const path = require('path'); | ||
const { hashElement } = require('../index.js'); | ||
// pass element name and folder path separately | ||
hasher.hashElement('node_modules', __dirname, function (error, hash) { | ||
if (error) return console.error('hashing failed:', error); | ||
console.log('Result for folder "node_modules" in directory "' + __dirname + '":'); | ||
console.log(hash.toString()); | ||
}); | ||
hashElement('test', path.join(__dirname, '..')) | ||
.then(hash => { | ||
console.log('Result for folder "../test":', hash.toString(), '\n'); | ||
}) | ||
.catch(error => { | ||
return console.error('hashing failed:', error); | ||
}); | ||
// pass element path directly | ||
hasher.hashElement(__dirname, function (error, hash) { | ||
if (error) return console.error('hashing failed:', error); | ||
console.log('Result for folder "' + __dirname + '":'); | ||
console.log(hash.toString()); | ||
}); | ||
hashElement(__dirname) | ||
.then(hash => { | ||
console.log(`Result for folder "${__dirname}":`); | ||
console.log(hash.toString(), '\n'); | ||
}) | ||
.catch(error => { | ||
return console.error('hashing failed:', error); | ||
}); | ||
// pass options (example: exclude dotFiles) | ||
var options = { excludes: ['**/.*'], match: { basename: false, path: true } }; | ||
hasher.hashElement(__dirname, options, function (error, hash) { | ||
console.log('Result for folder "' + __dirname + '":'); | ||
console.log(hash.toString()); | ||
}); | ||
// pass options (example: exclude dotFolders) | ||
const options = { encoding: 'hex', folders: { exclude: ['.*'] } }; | ||
hashElement(__dirname, options) | ||
.then(hash => { | ||
console.log('Result for folder "' + __dirname + '" (with options):'); | ||
console.log(hash.toString(), '\n'); | ||
}) | ||
.catch(error => { | ||
return console.error('hashing failed:', error); | ||
}); |
| ||
var crypto = require('crypto'); | ||
var path = require('path'); | ||
const crypto = require('crypto'), | ||
path = require('path'); | ||
var hashFile = require('../index.js'); | ||
const hashFolder = require('../index.js'); | ||
console.log('Known hash algorithms: ', '\'' + crypto.getHashes().join('\', \'') + '\''); | ||
console.log(`Known hash algorithms:\n'${crypto.getHashes().join(`', '`)}'\n`); | ||
const dir = path.resolve(__dirname, '../'); | ||
function checkPromise(promise) { | ||
promise.then(function (result) { | ||
console.log('Promise resolved:\n', result.toString(), '\n\n'); | ||
hashFolder.hashElement('README.md', dir) | ||
.then(result => { | ||
console.log('\nCreated a hash over a single file:'); | ||
console.log(result.toString()); | ||
}) | ||
.catch(function (reason) { | ||
console.error('Promise rejected due to:\n', reason, '\n\n'); | ||
.catch(reason => { | ||
console.error(`\nPromise rejected due to:\n${reason}\n\n`); | ||
}); | ||
} | ||
var file = 'README.md'; | ||
var dir = path.resolve(__dirname, '../'); | ||
console.log('\nCreate a hash over a single file:'); | ||
checkPromise(hashFile.hashElement(file, dir)); | ||
console.log('Create hash over a folder:'); | ||
//checkPromise(hashFile.hashElement(path.basename(dir), path.dirname(dir))); | ||
checkPromise(hashFile.hashElement('test', dir)); | ||
hashFolder.hashElement(dir, { | ||
files: { exclude: ['.*'], matchBasename: true }, | ||
folders: { include: ['examples', 'test'], matchBasename: true } | ||
}, (err, result) => { | ||
if (err) { | ||
console.error(`\nFailed to create a hash due to:\n${err}`); | ||
} else { | ||
console.log('\nCreated a hash over a folder:'); | ||
console.log(result.toString()); | ||
} | ||
}); |
399
index.js
@@ -1,226 +0,283 @@ | ||
"use strict" | ||
const crypto = require('crypto'), | ||
debug = require('debug'), | ||
minimatch = require('minimatch'), | ||
path = require('path'); | ||
var fs = require('graceful-fs'); | ||
var path = require('path'); | ||
var crypto = require('crypto'); | ||
var minimatch = require('minimatch'); | ||
if (typeof Promise === 'undefined') require('when/es6-shim/Promise'); | ||
var defaultOptions = { | ||
const defaultOptions = { | ||
algo: 'sha1', // see crypto.getHashes() for options | ||
encoding: 'base64', // 'base64', 'hex' or 'binary' | ||
excludes: [], | ||
match: { | ||
basename: true, | ||
path: true | ||
files: { | ||
exclude: [], | ||
include: [], | ||
matchBasename: true, | ||
matchPath: false | ||
}, | ||
folders: { | ||
exclude: [], | ||
include: [], | ||
matchBasename: true, | ||
matchPath: false | ||
} | ||
}; | ||
module.exports = { | ||
hashElement: hashElement | ||
} | ||
// Use the environment variable DEBUG to log output, e.g. `set DEBUG=fhash:*` | ||
const log = { | ||
match: debug('fhash:match'), | ||
params: (params) => { | ||
debug('fhash:parameters')(params); | ||
return params; | ||
} | ||
}; | ||
/** | ||
* Create a hash over a folder or file, using either promises or error-first-callbacks. | ||
* | ||
* Examples: | ||
* - hashElement(filename, folderpath, options, fn(err, hash) {}), hashElement(filename, folderpath, options); | ||
* - hashElement(path, fn(err, hash) {}), hashElement(path) | ||
* | ||
* @param {string} name - element name or an element's path | ||
* @param {string} [dir] - directory that contains the element (if omitted is generated from name) | ||
* @param {Object} [options] - Options | ||
* @param {string} [options.algo='sha1'] - checksum algorithm, see options in crypto.getHashes() | ||
* @param {string} [options.encoding='base64'] - encoding of the resulting hash. One of 'base64', 'hex' or 'binary' | ||
* @param {string[]} [options.excludes=[]] - Array of optional exclude file glob patterns, see minimatch doc | ||
* @param {bool} [options.match.basename=true] - Match the exclude patterns to the file/folder name | ||
* @param {bool} [options.match.path=true] - Match the exclude patterns to the file/folder path | ||
* @param {fn} [callback] - Error-first callback function | ||
*/ | ||
function hashElement(name, directoryPath, options, callback) { | ||
var promise = parseParameters(arguments); | ||
var callback = arguments[arguments.length-1]; | ||
function prep(fs, Promise) { | ||
function hashElement(name, dir, options, callback) { | ||
callback = arguments[arguments.length - 1]; | ||
return promise | ||
.then(function (result) { | ||
if (typeof callback === 'function') return callback(undefined, result); | ||
return result; | ||
}) | ||
.catch(function (reason) { | ||
if (typeof callback === 'function') return callback(reason); | ||
throw reason; | ||
}); | ||
} | ||
return parseParameters(arguments) | ||
.then(({ basename, dir, options }) => { | ||
// this is only used for the root level | ||
options.skipMatching = true; | ||
return hashElementPromise(basename, dir, options); | ||
}) | ||
.then(result => { | ||
if (typeof callback === 'function') { | ||
return callback(undefined, result); | ||
} else { | ||
return result; | ||
} | ||
}) | ||
.catch(reason => { | ||
if (typeof callback === 'function') { | ||
return callback(reason); | ||
} else { | ||
throw reason; | ||
} | ||
}); | ||
} | ||
function parseParameters(args) { | ||
var elementBasename = args[0], | ||
elementDirname = args[1], | ||
options = args[2]; | ||
if (!isString(elementBasename)) { | ||
return Promise.reject(new TypeError('First argument must be a string')); | ||
function hashElementPromise(basename, dirname, options) { | ||
return stat(path.join(dirname, basename)).then(stats => { | ||
if (stats.isDirectory()) { | ||
return hashFolderPromise(basename, dirname, options); | ||
} else if (stats.isFile()) { | ||
return hashFilePromise(basename, dirname, options); | ||
} else { | ||
return { | ||
name: basename, | ||
hash: 'unknown element type' | ||
}; | ||
} | ||
}); | ||
} | ||
if (!isString(elementDirname)) { | ||
elementDirname = path.dirname(elementBasename); | ||
elementBasename = path.basename(elementBasename); | ||
options = args[1]; | ||
function stat(filepath) { | ||
return new Promise((resolve, reject) => { | ||
fs.stat(filepath, (err, stats) => { | ||
if (err) { | ||
return reject(err); | ||
} else { | ||
return resolve(stats); | ||
} | ||
}); | ||
}); | ||
} | ||
// parse options (fallback default options) | ||
if (!isObject(options)) options = {}; | ||
['algo', 'encoding', 'excludes'].forEach(function(key) { | ||
if (!options.hasOwnProperty(key)) options[key] = defaultOptions[key]; | ||
}); | ||
if (!options.match) options.match = {}; | ||
if (!options.match.hasOwnProperty('basename')) options.match.basename = defaultOptions.match.basename; | ||
if (!options.match.hasOwnProperty('path')) options.match.path = defaultOptions.match.path; | ||
function hashFolderPromise(name, dir, options) { | ||
const folderPath = path.join(dir, name); | ||
if (!options.excludes || !Array.isArray(options.excludes) || options.excludes.length == 0) { | ||
options.excludes = undefined; | ||
} else { | ||
// combine globs into one single RegEx | ||
options.excludes = new RegExp(options.excludes.reduce(function (acc, exclude) { | ||
return acc + '|' + minimatch.makeRe(exclude).source; | ||
}, '').substr(1)); | ||
} | ||
//console.log('parsed options:', 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 hashElementPromise(elementBasename, elementDirname, options); | ||
} | ||
return readdir(folderPath).then(files => { | ||
const children = files.map(child => { | ||
return hashElementPromise(child, folderPath, options); | ||
}); | ||
function hashElementPromise(basename, dirname, options) { | ||
var filepath = path.join(dirname, basename); | ||
if (options.match.basename && options.excludes && options.excludes.test(basename)) { | ||
//console.log('regex', options.excludes, 'matched to', basename); | ||
return Promise.resolve(undefined); | ||
return Promise.all(children).then(children => { | ||
const hash = new HashedFolder(name, children.filter(notUndefined), options); | ||
return hash; | ||
}); | ||
}); | ||
} | ||
if (options.match.path && options.excludes && options.excludes.test(filepath)) { | ||
//console.log('regex', options.excludes, 'matched to', filepath); | ||
return Promise.resolve(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); | ||
} | ||
}); | ||
}); | ||
} | ||
return new Promise(function (resolve, reject, notify) { | ||
fs.stat(filepath, function (err, stats) { | ||
if (err) { | ||
return reject(err); | ||
} | ||
function hashFilePromise(name, dir, options) { | ||
const filePath = path.join(dir, name); | ||
if (stats.isDirectory()) { | ||
resolve(hashFolderPromise(basename, dirname, options)); | ||
} else if (stats.isFile()) { | ||
resolve(hashFilePromise(basename, dirname, options)); | ||
} else { | ||
resolve({ name: basename, hash: 'unknown element type' }); | ||
} | ||
}); | ||
}); | ||
} | ||
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; | ||
} | ||
return new Promise((resolve, reject) => { | ||
try { | ||
const hash = crypto.createHash(options.algo); | ||
hash.write(name); | ||
function hashFolderPromise(foldername, directoryPath, options) { | ||
var folderPath = path.join(directoryPath, foldername); | ||
const f = fs.createReadStream(filePath); | ||
f.pipe(hash, { end: false }); | ||
var notExcluded = function notExcluded(basename) { | ||
return !(options.match.basename && options.excludes && options.excludes.test(basename)); | ||
f.on('end', () => { | ||
const hashedFile = new HashedFile(name, hash, options.encoding); | ||
return resolve(hashedFile); | ||
}); | ||
} catch (ex) { | ||
return reject(ex); | ||
} | ||
}); | ||
} | ||
return new Promise(function (resolve, reject, notify) { | ||
fs.readdir(folderPath, function (err, files) { | ||
if (err) { | ||
var TAG = 'hashFolderPromise(' + foldername + ', ' + directoryPath + '):'; | ||
console.error(TAG, err); | ||
reject(err); | ||
function ignore(name, path, rules) { | ||
if (rules.exclude) { | ||
if (rules.matchBasename && rules.exclude.test(name)) { | ||
log.match(`exclude basename '${path}'`); | ||
return true; | ||
} else if (rules.matchPath && rules.exclude.test(path)) { | ||
log.match(`exclude path '${path}'`); | ||
return true; | ||
} | ||
} else if (rules.include) { | ||
if (rules.matchBasename && rules.include.test(name)) { | ||
log.match(`include basename '${path}'`); | ||
return false; | ||
} else if (rules.matchPath && rules.include.test(path)) { | ||
log.match(`include path '${path}'`); | ||
return false; | ||
} else { | ||
return true; | ||
} | ||
} | ||
var children = files.filter(notExcluded).map(function (child) { | ||
return hashElementPromise(child, folderPath, options); | ||
}); | ||
log.match(`unmatched '${path}'`); | ||
return false; | ||
} | ||
return Promise.all(children).then(function (children) { | ||
var hash = new HashedFolder(foldername, children.filter(notUndefined), options); | ||
resolve(hash); | ||
}); | ||
const HashedFolder = function HashedFolder(name, children, options) { | ||
this.name = name; | ||
this.children = children; | ||
const hash = crypto.createHash(options.algo); | ||
hash.write(name); | ||
children.forEach(child => { | ||
if (child.hash) { | ||
hash.write(child.hash); | ||
} | ||
}); | ||
}); | ||
} | ||
this.hash = hash.digest(options.encoding); | ||
}; | ||
function hashFilePromise(filename, directoryPath, options) { | ||
return new Promise(function (resolve, reject, notify) { | ||
try { | ||
var hash = crypto.createHash(options.algo); | ||
hash.write(filename); | ||
HashedFolder.prototype.toString = function (padding = '') { | ||
const first = `${padding}{ name: '${this.name}', hash: '${this.hash},'\n`; | ||
padding += ' '; | ||
var f = fs.createReadStream(path.join(directoryPath, filename)); | ||
f.pipe(hash, { end: false }); | ||
return `${first}${padding}children: ${this.childrenToString(padding)}}`; | ||
}; | ||
f.on('end', function () { | ||
var hashedFile = new HashedFile(filename, hash, options); | ||
resolve(hashedFile); | ||
}); | ||
} catch (ex) { | ||
reject(ex); | ||
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); | ||
}; | ||
var HashedFolder = function (name, children, options) { | ||
this.name = name; | ||
this.children = children; | ||
HashedFile.prototype.toString = function (padding = '') { | ||
return padding + '{ name: \'' + this.name + '\', hash: \'' + this.hash + '\' }'; | ||
}; | ||
var hash = crypto.createHash(options.algo); | ||
hash.write(name); | ||
children.forEach(function (child) { | ||
if (child.hash) { | ||
hash.write(child.hash); | ||
} | ||
}); | ||
this.hash = hash.digest(options.encoding); | ||
return hashElement; | ||
} | ||
HashedFolder.prototype.toString = function (padding) { | ||
if (typeof padding === 'undefined') padding = ""; | ||
var str = padding + '{ name: \'' + this.name + '\', hash: \'' + this.hash + '\'\n'; | ||
padding += ' '; | ||
str += padding + 'children: '; | ||
if (this.children.length === 0) { | ||
str += '[]'; | ||
} else { | ||
var nextPadding = padding + " "; | ||
var childElements = this.children.map(function (child) { return child.toString(nextPadding); }); | ||
str += '[\n' + childElements.join('\n') + '\n' + padding + ']'; | ||
function parseParameters(args) { | ||
let basename = args[0], | ||
dir = args[1], | ||
options_ = args[2]; | ||
if (!isString(basename)) { | ||
return Promise.reject(new TypeError('First argument must be a string')); | ||
} | ||
return str + ' }'; | ||
} | ||
if (!isString(dir)) { | ||
dir = path.dirname(basename); | ||
basename = path.basename(basename); | ||
options_ = args[1]; | ||
} | ||
// 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) | ||
}; | ||
var HashedFile = function (name, hash, options) { | ||
this.name = name; | ||
this.hash = hash.digest(options.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) { | ||
if (typeof padding === 'undefined') padding = ""; | ||
return padding + '{ name: \'' + this.name + '\', hash: \'' + this.hash + '\' }'; | ||
return Promise.resolve(log.params({ basename, dir, options })); | ||
} | ||
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 (!globs || !Array.isArray(globs) || globs.length === 0) { | ||
return undefined; | ||
} else { | ||
// combine globs into one single RegEx | ||
return new RegExp(globs.reduce((acc, exclude) => { | ||
return acc + '|' + minimatch.makeRe(exclude).source; | ||
}, '').substr(1)); | ||
} | ||
} | ||
module.exports = { | ||
hashElement: prep(require("graceful-fs"), Promise), | ||
// exposed for testing | ||
prep: prep, | ||
parseParameters: parseParameters | ||
}; |
{ | ||
"name": "folder-hash", | ||
"version": "1.1.2", | ||
"version": "2.0.0", | ||
"description": "Create a hash checksum over a folder and its content - its children and their content", | ||
@@ -22,5 +22,5 @@ "main": "index.js", | ||
"dependencies": { | ||
"debug": "^3.1.0", | ||
"graceful-fs": "~4.1.11", | ||
"minimatch": "~3.0.4", | ||
"when": "~3.7.8" | ||
"minimatch": "~3.0.4" | ||
}, | ||
@@ -32,8 +32,8 @@ "devDependencies": { | ||
"jsdoc": "^3.4.3", | ||
"mocha": "^3.2.0", | ||
"rimraf": "^2.5.2" | ||
"memfs": "^2.8.0", | ||
"mocha": "^5.0.5" | ||
}, | ||
"engines": { | ||
"node": ">=0.10.5" | ||
"node": ">=7.0.0" | ||
} | ||
} |
259
README.md
Create a hash checksum over a folder or a file. | ||
The hashes are propagated upwards, the hash that is returned for a folder is generated over all the hashes of its children. | ||
The hashes are generated with the _sha1_ algorithm and returned in _base64_ encoding. | ||
The hashes are generated with the _sha1_ algorithm and returned in _base64_ encoding by default. | ||
The returned information looks like this: | ||
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`. | ||
### Simple example | ||
See file *./examples/readme-example1.js*. | ||
This example excludes all files and folders starting with a dot, (e.g. *.git/* and *.gitignore*), the *node_modules* folder. | ||
```js | ||
{ name: 'test', | ||
hash: 'qmUXLCsTQGOEF6p0w9V78MC7sJI=', | ||
children: [ | ||
{ name: 'helper', | ||
hash: 'x1CX3yVH3UuLTw7zcSitSs/PbGE=', | ||
children: [ | ||
{ name: 'helper.js', hash: 'pHYwd8k/oZV01oABTz9MC8KovkU=' } | ||
] }, | ||
{ name: 'test.js', hash: 'L/vqpdQhxmD5w62k24m4TuZJ1PM=' } | ||
] | ||
} | ||
const { hashElement } = require('folder-hash'); | ||
const options = { | ||
folders: { exclude: ['.*', 'node_modules', 'test_coverage'] }, | ||
files: { include: ['*.js', '*.json'] } | ||
}; | ||
console.log('Creating a hash over the current folder:'); | ||
hashElement('.', options) | ||
.then(hash => { | ||
console.log(hash.toString()); | ||
}) | ||
.catch(error => { | ||
return console.error('hashing failed:', error); | ||
}); | ||
``` | ||
Each file returns a name and a hash, and each folder returns additionally an array of children (file or folder elements). | ||
The returned information looks for example like this: | ||
``` | ||
Creating a hash over the current folder: | ||
{ name: '.', hash: 'YZOrKDx9LCLd8X39PoFTflXGpRU=,' | ||
children: [ | ||
{ name: 'examples', hash: 'aG8wg8np5SGddTnw1ex74PC9EnM=,' | ||
children: [ | ||
{ name: 'readme-example1.js', hash: 'Xlw8S2iomJWbxOJmmDBnKcauyQ8=' } | ||
{ name: 'readme-with-callbacks.js', hash: 'ybvTHLCQBvWHeKZtGYZK7+6VPUw=' } | ||
{ name: 'readme-with-promises.js', hash: '43i9tE0kSFyJYd9J2O0nkKC+tmI=' } | ||
{ name: 'sample.js', hash: 'PRTD9nsZw3l73O/w5B2FH2qniFk=' } | ||
]} | ||
{ name: 'index.js', hash: 'kQQWXdgKuGfBf7ND3rxjThTLVNA=' } | ||
{ name: 'package.json', hash: 'w7F0S11l6VefDknvmIy8jmKx+Ng=' } | ||
{ name: 'test', hash: 'H5x0JDoV7dEGxI65e8IsencDZ1A=,' | ||
children: [ | ||
{ name: 'parameters.js', hash: '3gCEobqzHGzQiHmCDe5yX8weq7M=' } | ||
{ name: 'test.js', hash: 'kg7p8lbaVf1CPtWLAIvkHkdu1oo=' } | ||
]} | ||
]} | ||
``` | ||
## Usage | ||
First, install folder-hash with `npm install --save folder-hash`. | ||
### With promises | ||
It is also possible to only match the full path and not the basename. The same configuration could look like this: | ||
_But unfortunately *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 | ||
} | ||
}; | ||
``` | ||
### Other examples using promises | ||
See file *./examples/readme-with-promises.js* | ||
```js | ||
var hasher = require('folder-hash'); | ||
const path = require('path'); | ||
const { hashElement } = require('folder-hash'); | ||
// pass element name and folder path separately | ||
hasher.hashElement('node_modules', __dirname).then(function (hash) { | ||
console.log('Result for folder "node_modules" in directory "' + __dirname + '":'); | ||
console.log(hash.toString()); | ||
}); | ||
hashElement('test', path.join(__dirname, '..')) | ||
.then(hash => { | ||
console.log('Result for folder "../test":', hash.toString(), '\n'); | ||
}) | ||
.catch(error => { | ||
return console.error('hashing failed:', error); | ||
}); | ||
// pass element path directly | ||
hasher.hashElement(__dirname).then(function (hash) { | ||
console.log('Result for folder "' + __dirname + '":'); | ||
console.log(hash.toString()); | ||
}); | ||
// pass options (example: exclude dotFiles) | ||
var options = { excludes: ['.*'], match: { basename: true, path: false } }; | ||
hasher.hashElement(__dirname, options) | ||
.then(function (hash) { | ||
console.log('Result for folder "' + __dirname + '":'); | ||
console.log(hash.toString()); | ||
}) | ||
.catch(function (error) { | ||
return console.error('hashing failed:', error); | ||
}); | ||
hashElement(__dirname) | ||
.then(hash => { | ||
console.log(`Result for folder "${__dirname}":`); | ||
console.log(hash.toString(), '\n'); | ||
}) | ||
.catch(error => { | ||
return console.error('hashing failed:', error); | ||
}); | ||
// pass options (example: exclude dotFolders) | ||
const options = { encoding: 'hex', folders: { exclude: ['.*'] } }; | ||
hashElement(__dirname, options) | ||
.then(hash => { | ||
console.log('Result for folder "' + __dirname + '" (with options):'); | ||
console.log(hash.toString(), '\n'); | ||
}) | ||
.catch(error => { | ||
return console.error('hashing failed:', error); | ||
}); | ||
``` | ||
### With callbacks | ||
### Other examples using error-first callbacks | ||
See *./examples/readme-with-callbacks.js* | ||
```js | ||
var hasher = require('folder-hash'); | ||
const path = require('path'); | ||
const { hashElement } = require('folder-hash'); | ||
// pass element name and folder path separately | ||
hasher.hashElement('node_modules', __dirname, function (error, hash) { | ||
if (error) return console.error('hashing failed:', error); | ||
console.log('Result for folder "node_modules" in directory "' + __dirname + '":'); | ||
console.log(hash.toString()); | ||
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'); | ||
} | ||
}); | ||
// pass element path directly | ||
hasher.hashElement(__dirname, function (error, hash) { | ||
if (error) return console.error('hashing failed:', error); | ||
console.log('Result for folder "' + __dirname + '":'); | ||
console.log(hash.toString()); | ||
hashElement(__dirname, (error, hash) => { | ||
if (error) { | ||
return console.error('hashing failed:', error); | ||
} else { | ||
console.log('Result for folder "' + __dirname + '":'); | ||
console.log(hash.toString(), '\n'); | ||
} | ||
}); | ||
// pass options (example: exclude dotFiles) | ||
var options = { excludes: ['**/.*'], match: { basename: false, path: true } }; | ||
hasher.hashElement(__dirname, options, function (error, hash) { | ||
console.log('Result for folder "' + __dirname + '":'); | ||
console.log(hash.toString()); | ||
const options = { algo: 'md5', files: { exclude: ['.*'], matchBasename: true } }; | ||
hashElement(__dirname, options, (error, hash) => { | ||
if (error) { | ||
return console.error('hashing failed:', error); | ||
} else { | ||
console.log('Result for folder "' + __dirname + '":'); | ||
console.log(hash.toString()); | ||
} | ||
}); | ||
@@ -105,3 +176,3 @@ ``` | ||
</td> | ||
<td>directory that contains the element (if omitted is generated from name)</td> | ||
<td>directory that contains the element (generated from name if omitted)</td> | ||
</tr> | ||
@@ -134,2 +205,22 @@ <tr> | ||
#### Options object properties | ||
##### Default values | ||
```js | ||
{ | ||
algo: 'sha1', // see crypto.getHashes() for options | ||
encoding: 'base64', // 'base64', 'hex' or 'binary' | ||
files: { | ||
exclude: [], | ||
include: [], | ||
matchBasename: true, | ||
matchPath: false | ||
}, | ||
folders: { | ||
exclude: [], | ||
include: [], | ||
matchBasename: true, | ||
matchPath: false | ||
} | ||
} | ||
``` | ||
<table> | ||
@@ -173,4 +264,43 @@ <thead> | ||
<tr> | ||
<td>excludes</td> | ||
<td>files</td> | ||
<td> | ||
<span>Object</span> | ||
</td> | ||
<td> | ||
<optional><br> | ||
</td> | ||
<td colspan="2"> | ||
Rules object (see below) | ||
</td> | ||
</tr> | ||
<tr> | ||
<td>folders</td> | ||
<td> | ||
<span>Object</span> | ||
</td> | ||
<td> | ||
<optional><br> | ||
</td> | ||
<td colspan="2"> | ||
Rules object (see below) | ||
</td> | ||
</tr> | ||
</tbody> | ||
</table> | ||
#### Rules object properties | ||
<table> | ||
<thead> | ||
<tr> | ||
<th>Name</th> | ||
<th>Type</th> | ||
<th>Attributes</th> | ||
<th>Default</th> | ||
<th>Description</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
<tr> | ||
<td>exclude</td> | ||
<td> | ||
<span>Array.<string></span> | ||
@@ -184,7 +314,20 @@ </td> | ||
</td> | ||
<td>Array of optional exclude file glob patterns, see minimatch doc</td> | ||
<td>Array of optional exclude glob patterns, see <a href="https://github.com/isaacs/minimatch#features">minimatch doc</a></td> | ||
</tr> | ||
<tr> | ||
<td>match.basename</td> | ||
<td>include</td> | ||
<td> | ||
<span>Array.<string></span> | ||
</td> | ||
<td> | ||
<optional><br> | ||
</td> | ||
<td> | ||
[] | ||
</td> | ||
<td>Array of optional include glob patterns, see <a href="https://github.com/isaacs/minimatch#features">minimatch doc</a></td> | ||
</tr> | ||
<tr> | ||
<td>matchBasename</td> | ||
<td> | ||
<span>bool</span> | ||
@@ -198,6 +341,6 @@ </td> | ||
</td> | ||
<td>Match the exclude patterns to the file/folder name</td> | ||
<td>Match the glob patterns to the file/folder name</td> | ||
</tr> | ||
<tr> | ||
<td>match.path</td> | ||
<td>matchPath</td> | ||
<td> | ||
@@ -210,5 +353,5 @@ <span>bool</span> | ||
<td> | ||
true | ||
false | ||
</td> | ||
<td>Match the exclude patterns to the file/folder path</td> | ||
<td>Match the glob patterns to the file/folder path</td> | ||
</tr> | ||
@@ -215,0 +358,0 @@ </tbody> |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Mixed license
License(Experimental) Package contains multiple licenses.
Found 1 instance in 1 package
0
765
387
63248
12
+ Addeddebug@^3.1.0
+ Addeddebug@3.2.7(transitive)
+ Addedms@2.1.3(transitive)
- Removedwhen@~3.7.8
- Removedwhen@3.7.8(transitive)