From 488bce5c9874680be4202408c1858b289e627913 Mon Sep 17 00:00:00 2001 From: Bart Riepe Date: Fri, 7 Jul 2023 16:08:35 +0900 Subject: [PATCH 1/2] fix: excludes the passed path for purposes of matching inclusion/exclusion folders The same folder accessed from a different working directory will include/exclude in the same way --- index.js | 43 +++++++++++++++++++++++++++---------------- test/folders.js | 25 +++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 16 deletions(-) diff --git a/index.js b/index.js index 59d9e0c..d0370b9 100644 --- a/index.js +++ b/index.js @@ -49,6 +49,7 @@ function prep(fs) { function hashElement(name, dir, options, callback) { callback = arguments[arguments.length - 1]; + const base = name; return parseParameters(arguments) .then(({ basename, dir, options }) => { @@ -60,7 +61,7 @@ function prep(fs) { stats.name = basename; return stats; }) - .then(stats => hashElementPromise(stats, dir, options, true)); + .then(stats => hashElementPromise(stats, dir, base, options, true)); }) .then(result => { if (isFunction(callback)) { @@ -82,18 +83,19 @@ function prep(fs) { /** * @param {fs.Stats} stats folder element, can also be of type fs.Dirent * @param {string} dirname + * @param {string} base * @param {Options} options * @param {boolean} isRootElement */ - function hashElementPromise(stats, dirname, options, isRootElement = false) { + function hashElementPromise(stats, dirname, base, options, isRootElement = false) { const name = stats.name; let promise = undefined; if (stats.isDirectory()) { - promise = hashFolderPromise(name, dirname, options, isRootElement); + promise = hashFolderPromise(name, dirname, base, options, isRootElement); } else if (stats.isFile()) { - promise = hashFilePromise(name, dirname, options, isRootElement); + promise = hashFilePromise(name, dirname, base, options, isRootElement); } else if (stats.isSymbolicLink()) { - promise = hashSymLinkPromise(name, dirname, options, isRootElement); + promise = hashSymLinkPromise(name, dirname, base, options, isRootElement); } else { log.err('hashElementPromise cannot handle ', stats); return Promise.resolve({ name, hash: 'Error: unknown element type' }); @@ -106,7 +108,7 @@ function prep(fs) { const promise = new Promise((resolve, reject) => { queue.push(() => { log.queue(`Will processs queued ${dirname}/${name}`); - return hashElementPromise(stats, dirname, options, isRootElement) + return hashElementPromise(stats, dirname, base, options, isRootElement) .then(ok => resolve(ok)) .catch(err => reject(err)); }); @@ -129,7 +131,7 @@ function prep(fs) { runnables.forEach(run => run()); } - async function hashFolderPromise(name, dir, options, isRootElement = false) { + async function hashFolderPromise(name, dir, base, options, isRootElement = false) { const folderPath = path.join(dir, name); let ignoreBasenameOnce = options.ignoreBasenameOnce; delete options.ignoreBasenameOnce; @@ -138,7 +140,7 @@ function prep(fs) { // this is currently only used for the root folder log.match(`skipped '${folderPath}'`); delete options.skipMatching; - } else if (ignore(name, folderPath, options.folders)) { + } else if (ignore(name, folderPath, base, options.folders)) { return undefined; } @@ -146,7 +148,7 @@ function prep(fs) { const children = await Promise.all( files .sort((a, b) => a.name.localeCompare(b.name)) - .map(child => hashElementPromise(child, folderPath, options)), + .map(child => hashElementPromise(child, folderPath, base, options)), ); if (ignoreBasenameOnce) options.ignoreBasenameOnce = true; @@ -154,14 +156,14 @@ function prep(fs) { return hash; } - function hashFilePromise(name, dir, options, isRootElement = false) { + function hashFilePromise(name, dir, base, options, isRootElement = false) { const filePath = path.join(dir, name); 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)) { + } else if (ignore(name, filePath, base, options.files)) { return Promise.resolve(undefined); } @@ -195,14 +197,14 @@ function prep(fs) { }); } - async function hashSymLinkPromise(name, dir, options, isRootElement = false) { + async function hashSymLinkPromise(name, dir, base, 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); + return symLinkResolve(name, dir, base, target, options, isRootElement); } } else { log.symlink('skipping symbolic link'); @@ -225,7 +227,7 @@ function prep(fs) { return Promise.resolve(new HashedFile(name, hash, options.encoding)); } - async function symLinkResolve(name, dir, target, options, isRootElement) { + async function symLinkResolve(name, dir, base, target, options, isRootElement) { delete options.skipMatching; // only used for the root level if (options.symbolicLinks.ignoreBasename) { options.ignoreBasenameOnce = true; @@ -234,7 +236,7 @@ function prep(fs) { try { const stats = await fs.promises.stat(path.join(dir, name)); stats.name = name; - const temp = await hashElementPromise(stats, dir, options, isRootElement); + const temp = await hashElementPromise(stats, dir, base, options, isRootElement); if (!options.symbolicLinks.ignoreTargetPath) { const hash = crypto.createHash(options.algo); @@ -265,7 +267,16 @@ function prep(fs) { } } - function ignore(name, path, rules) { + function stripBase(path, base) { + let result = path.replace(base, '') + if (result.startsWith('/')) result = result.substring(1) + + return result + } + + function ignore(name, path, root, rules) { + path = stripBase(path, root) + if (rules.exclude) { if (rules.matchBasename && rules.exclude(name)) { log.match(`exclude basename '${name}'`); diff --git a/test/folders.js b/test/folders.js index d4b9aab..d1cc659 100644 --- a/test/folders.js +++ b/test/folders.js @@ -55,6 +55,31 @@ describe('Generating a hash over a folder, it', function () { return hashElement('abc', options).then(checkChildren); }); + it('ignores base directory for purposes of exclude function', function () { + const hashElement = prep( + Volume.fromJSON({ + 'abc/def': 'abc/def', + 'abc/ghi/jkl/file.js': 'content', + 'abc/ghi/jkl/file2.js': 'content', + 'abc/ghi/folder/data.json': 'content', + 'abc/ghi/folder/subfolder/today.log': 'content', + }), + ); + + const options = { + folders: { + exclude: ['jkl'], + }, + }; + + const checkChildren = current => { + current.children.length.should.equal(1); + current.children[0].name.should.equal('folder'); + }; + + return hashElement('abc/ghi', options).then(checkChildren); + }); + it('generates different hashes if the folders have the same content but different names', function () { const hashElement = prep( Volume.fromJSON({ From 35c02fa7c520e4a836e71224f4cdb563838c4641 Mon Sep 17 00:00:00 2001 From: Bart Riepe Date: Fri, 7 Jul 2023 23:51:14 +0900 Subject: [PATCH 2/2] feat: convert(ish) to typescript --- .gitignore | 4 + cli.js => cli.ts | 6 +- examples/readme-example1.js | 2 +- examples/readme-with-callbacks.js | 2 +- examples/readme-with-promises.js | 2 +- examples/sample.js | 2 +- index.js => index.ts | 174 ++++++++++++++++++------------ package-lock.json | 95 +++++++++++++--- package.json | 24 +++-- tsconfig.json | 9 ++ 10 files changed, 224 insertions(+), 96 deletions(-) rename cli.js => cli.ts (93%) rename index.js => index.ts (71%) create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore index 55c546c..1fae431 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,7 @@ test_coverage/** .nyc_output .npmrc .idea +index.d.ts +index.js +cli.d.ts +cli.js diff --git a/cli.js b/cli.ts similarity index 93% rename from cli.js rename to cli.ts index 5a81fe5..78328d7 100644 --- a/cli.js +++ b/cli.ts @@ -1,5 +1,5 @@ -const fs = require('fs'); -const lib = require('./index'); +import * as fs from 'fs'; +import * as lib from './index'; function program(cliArgs) { let args; @@ -64,7 +64,7 @@ function parseArgs(args) { } function error(err) { - console.error('ERROR:', ex.message || ex.name || ex); + console.error('ERROR:', err.message || err.name || err); process.exit(1); } diff --git a/examples/readme-example1.js b/examples/readme-example1.js index 33a1dde..b27c645 100644 --- a/examples/readme-example1.js +++ b/examples/readme-example1.js @@ -1,7 +1,7 @@ // execute from the base folder // node examples\readme-example1.js -const { hashElement } = require('../index.js'); +const { hashElement } = require('../index.ts'); const options = { folders: { exclude: ['.*', 'node_modules', 'test_coverage'] }, diff --git a/examples/readme-with-callbacks.js b/examples/readme-with-callbacks.js index 973c091..0f61dec 100644 --- a/examples/readme-with-callbacks.js +++ b/examples/readme-with-callbacks.js @@ -2,7 +2,7 @@ // node examples\readme-with-callbacks.js const path = require('path'); -const { hashElement } = require('../index.js'); +const { hashElement } = require('../index.ts'); // pass element name and folder path separately hashElement('test', path.join(__dirname, '..'), (error, hash) => { diff --git a/examples/readme-with-promises.js b/examples/readme-with-promises.js index de63538..85d2ad3 100644 --- a/examples/readme-with-promises.js +++ b/examples/readme-with-promises.js @@ -2,7 +2,7 @@ // node examples\readme-with-promises.js const path = require('path'); -const { hashElement } = require('../index.js'); +const { hashElement } = require('../index.ts'); // pass element name and folder path separately hashElement('test', path.join(__dirname, '..')) diff --git a/examples/sample.js b/examples/sample.js index ac63a82..bc46560 100644 --- a/examples/sample.js +++ b/examples/sample.js @@ -1,7 +1,7 @@ const crypto = require('crypto'), path = require('path'); -const hashFolder = require('../index.js'); +const hashFolder = require('../index.ts'); console.log(`Known hash algorithms:\n'${crypto.getHashes().join(`', '`)}'\n`); diff --git a/index.js b/index.ts similarity index 71% rename from index.js rename to index.ts index d0370b9..e130abe 100644 --- a/index.js +++ b/index.ts @@ -1,7 +1,8 @@ -const crypto = require('crypto'), - debug = require('debug'), - minimatch = require('minimatch'), - path = require('path'); +import * as crypto from 'crypto'; +import debug from 'debug' +import {minimatch} from 'minimatch' +import * as path from 'path' +import type * as fsModule from 'fs' const defaultOptions = { algo: 'sha1', // see crypto.getHashes() for options @@ -43,11 +44,41 @@ const log = { queue: debug('fhash:queue'), }; -function prep(fs) { +type HashedEntity = HashedFolder | HashedFile + +interface MatchOptions { + exclude: string[] + include: string[] + matchBasename: boolean + matchPath: boolean + ignoreBasename: boolean + ignoreRootName: boolean +} +type Options = { + skipMatching: boolean + algo: string + encoding: crypto.BinaryToTextEncoding + ignoreBasenameOnce?: boolean + files: MatchOptions + folders: MatchOptions + symbolicLinks: { + include: boolean + ignoreBasename: boolean + ignoreTargetPath: boolean + ignoreTargetContent: boolean + ignoreTargetContentAfterError: boolean + } +} +type Callback = (err: Error, result?: HashedEntity) => void + +export function prep(fs: typeof fsModule) { let queue = []; let queueTimer = undefined; - function hashElement(name, dir, options, callback) { + function hashElement(name: string, options?: Options, callback?: Callback): Promise; + function hashElement(name: string, dir: string, callback?: Callback): Promise; + function hashElement(name: string, dir: string, options?: Options, callback?: Callback): Promise; + function hashElement(name: string, dir: string | Options, options?: Options | Callback, callback?: Callback): Promise { callback = arguments[arguments.length - 1]; const base = name; @@ -58,10 +89,11 @@ function prep(fs) { return fs.promises .lstat(path.join(dir, basename)) .then(stats => { - stats.name = basename; + // @ts-expect-error + stats.name = basename return stats; }) - .then(stats => hashElementPromise(stats, dir, base, options, true)); + .then((stats: fsModule.Stats & {name: string}) => hashElementPromise(stats, dir, base, options, true)); }) .then(result => { if (isFunction(callback)) { @@ -87,7 +119,7 @@ function prep(fs) { * @param {Options} options * @param {boolean} isRootElement */ - function hashElementPromise(stats, dirname, base, options, isRootElement = false) { + function hashElementPromise(stats: (fsModule.Stats & {name: string}) | fsModule.Dirent, dirname: string, base: string, options: Options, isRootElement = false): Promise { const name = stats.name; let promise = undefined; if (stats.isDirectory()) { @@ -131,7 +163,7 @@ function prep(fs) { runnables.forEach(run => run()); } - async function hashFolderPromise(name, dir, base, options, isRootElement = false) { + async function hashFolderPromise(name: string, dir: string, base: string, options: Options, isRootElement = false) { const folderPath = path.join(dir, name); let ignoreBasenameOnce = options.ignoreBasenameOnce; delete options.ignoreBasenameOnce; @@ -156,7 +188,7 @@ function prep(fs) { return hash; } - function hashFilePromise(name, dir, base, options, isRootElement = false) { + function hashFilePromise(name: string, dir: string, base: string, options: Options, isRootElement = false) { const filePath = path.join(dir, name); if (options.skipMatching) { @@ -197,7 +229,7 @@ function prep(fs) { }); } - async function hashSymLinkPromise(name, dir, base, options, isRootElement = false) { + async function hashSymLinkPromise(name: string, dir: string, base: string, options: Options, isRootElement = false) { const target = await fs.promises.readlink(path.join(dir, name)); log.symlink(`handling symbolic link ${name} -> ${target}`); if (options.symbolicLinks.include) { @@ -212,7 +244,7 @@ function prep(fs) { } } - function symLinkIgnoreTargetContent(name, target, options, isRootElement) { + function symLinkIgnoreTargetContent(name, target, options: Options, isRootElement) { delete options.skipMatching; // only used for the root level log.symlink('ignoring symbolic link target content'); const hash = crypto.createHash(options.algo); @@ -234,8 +266,8 @@ function prep(fs) { } try { - const stats = await fs.promises.stat(path.join(dir, name)); - stats.name = name; + const stats = await fs.promises.stat(path.join(dir, name)) as fsModule.Stats & {name: string}; + stats.name = name const temp = await hashElementPromise(stats, dir, base, options, isRootElement); if (!options.symbolicLinks.ignoreTargetPath) { @@ -274,7 +306,7 @@ function prep(fs) { return result } - function ignore(name, path, root, rules) { + function ignore(name: string, path: string, root: string, rules) { path = stripBase(path, root) if (rules.exclude) { @@ -306,7 +338,7 @@ function prep(fs) { return hashElement; } -function parseParameters(args) { +export function parseParameters(args): Promise<{basename: string, dir: string, options: Options}> { let basename = args[0], dir = args[1], options_ = args[2]; @@ -328,7 +360,6 @@ function parseParameters(args) { 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), }; @@ -341,55 +372,65 @@ function parseParameters(args) { return Promise.resolve(log.params({ basename, dir, options })); } -const HashedFolder = function HashedFolder(name, children, options, isRootElement = false) { - this.name = name; - this.children = children; +class HashedFolder { + name: string + hash: string + children: HashedEntity[] + constructor(name: string, children: HashedEntity[], options, isRootElement = false) { + this.name = name; + this.children = children; - 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); + 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); + } + }); - this.hash = hash.digest(options.encoding); -}; + this.hash = hash.digest(options.encoding); + } -HashedFolder.prototype.toString = function (padding = '') { - const first = `${padding}{ name: '${this.name}', hash: '${this.hash}',\n`; - padding += ' '; + toString(padding = '') { + const first = `${padding}{ name: '${this.name}', hash: '${this.hash}',\n`; + padding += ' '; - return `${first}${padding}children: ${this.childrenToString(padding)}}`; -}; + return `${first}${padding}children: ${this.childrenToString(padding)}}`; + } -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}]`; + childrenToString(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); -}; +class HashedFile { + name: string + hash: string -HashedFile.prototype.toString = function (padding = '') { - return padding + "{ name: '" + this.name + "', hash: '" + this.hash + "' }"; -}; + constructor(name: string, hash: crypto.Hash, encoding: crypto.BinaryToTextEncoding) { + this.name = name; + this.hash = hash.digest(encoding); + } + + toString(padding = '') { + return padding + "{ name: '" + this.name + "', hash: '" + this.hash + "' }"; + } +} function isFunction(any) { return typeof any === 'function'; @@ -417,18 +458,17 @@ function reduceGlobPatterns(globs) { const regex = new RegExp( globs .reduce((acc, exclude) => { - return acc + '|' + minimatch.makeRe(exclude).source; + const re = minimatch.makeRe(exclude) + if (!re) { + throw new Error(`Failed to parse glob: ${exclude}`); + } + return acc + '|' + re.source; }, '') - .substr(1), + .substring(1), ); return param => regex.test(param); } } -module.exports = { - defaults: defaultOptions, - hashElement: prep(require('fs')), - // exposed for testing - prep, - parseParameters, -}; +export const hashElement = prep(require('fs')) +export const defaults = defaultOptions diff --git a/package-lock.json b/package-lock.json index 30a0e4f..700aece 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,21 +1,23 @@ { - "name": "folder-hash", - "version": "4.0.4", + "name": "@aeolun/folder-hash", + "version": "4.0.5", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "folder-hash", - "version": "4.0.4", + "name": "@aeolun/folder-hash", + "version": "4.0.5", "license": "MIT", "dependencies": { - "debug": "^4.3.3", - "minimatch": "~5.1.2" + "debug": "^4.3.4", + "minimatch": "^9.0.3" }, "bin": { "folder-hash": "bin/folder-hash" }, "devDependencies": { + "@types/debug": "^4.1.8", + "@types/node": "^20.4.0", "chai": "^4.3.6", "chai-as-promised": "^7.1.1", "clone": "^2.1.2", @@ -24,7 +26,8 @@ "memfs": "^3.4.1", "mocha": "^9.2.0", "nyc": "^15.1.0", - "prettier": "~2.8.2" + "prettier": "~2.8.2", + "typescript": "^5" }, "engines": { "node": ">=10.10.0" @@ -375,6 +378,15 @@ "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", "dev": true }, + "node_modules/@types/debug": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.8.tgz", + "integrity": "sha512-/vPO1EPOs306Cvhwv7KfVfYvOJqA/S/AXjaHQiJboCZzcNDb+TIJFN9/2C9DZ//ijSKWioNyUxD792QmDJ+HKQ==", + "dev": true, + "dependencies": { + "@types/ms": "*" + } + }, "node_modules/@types/linkify-it": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.2.tgz", @@ -397,6 +409,18 @@ "integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==", "dev": true }, + "node_modules/@types/ms": { + "version": "0.7.31", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", + "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.4.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.0.tgz", + "integrity": "sha512-jfT7iTf/4kOQ9S7CHV9BIyRaQqHu67mOjsIQBC3BKZvzvUB6zLxEwJ6sBE3ozcvP8kF6Uk5PXN0Q+c0dfhGX0g==", + "dev": true + }, "node_modules/@ungap/promise-all-settled": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", @@ -1736,14 +1760,17 @@ } }, "node_modules/minimatch": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.2.tgz", - "integrity": "sha512-bNH9mmM9qsJ2X4r2Nat1B//1dJVcn3+iBLa3IgqJ7EbGaDNepL9QSHOxN4ng33s52VMMhhIfgCYDk3C4ZmlDAg==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dependencies": { "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=10" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/mkdirp": { @@ -2658,6 +2685,19 @@ "is-typedarray": "^1.0.0" } }, + "node_modules/typescript": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", + "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/uc.micro": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", @@ -3293,6 +3333,15 @@ "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", "dev": true }, + "@types/debug": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.8.tgz", + "integrity": "sha512-/vPO1EPOs306Cvhwv7KfVfYvOJqA/S/AXjaHQiJboCZzcNDb+TIJFN9/2C9DZ//ijSKWioNyUxD792QmDJ+HKQ==", + "dev": true, + "requires": { + "@types/ms": "*" + } + }, "@types/linkify-it": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.2.tgz", @@ -3315,6 +3364,18 @@ "integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==", "dev": true }, + "@types/ms": { + "version": "0.7.31", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", + "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==", + "dev": true + }, + "@types/node": { + "version": "20.4.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.0.tgz", + "integrity": "sha512-jfT7iTf/4kOQ9S7CHV9BIyRaQqHu67mOjsIQBC3BKZvzvUB6zLxEwJ6sBE3ozcvP8kF6Uk5PXN0Q+c0dfhGX0g==", + "dev": true + }, "@ungap/promise-all-settled": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", @@ -4350,9 +4411,9 @@ } }, "minimatch": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.2.tgz", - "integrity": "sha512-bNH9mmM9qsJ2X4r2Nat1B//1dJVcn3+iBLa3IgqJ7EbGaDNepL9QSHOxN4ng33s52VMMhhIfgCYDk3C4ZmlDAg==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "requires": { "brace-expansion": "^2.0.1" } @@ -5071,6 +5132,12 @@ "is-typedarray": "^1.0.0" } }, + "typescript": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", + "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", + "dev": true + }, "uc.micro": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", diff --git a/package.json b/package.json index f9f472e..7c511dc 100644 --- a/package.json +++ b/package.json @@ -1,17 +1,20 @@ { - "name": "folder-hash", - "version": "4.0.4", + "name": "@aeolun/folder-hash", + "version": "4.0.5", "description": "Create a hash checksum over a folder and its content - its children and their content", "main": "index.js", + "types": "index.d.ts", "bin": { "folder-hash": "bin/folder-hash" }, "scripts": { "start": "node sample.js", - "test": "mocha --reporter spec test", - "cover": "nyc mocha test", + "test": "npm run compile && mocha --reporter spec test", + "cover": "npm run compile && nyc mocha test", "format": "prettier --write *.js examples/ test/", - "doc": "./node_modules/.bin/jsdoc index.js -R README.md -d doc" + "doc": "./node_modules/.bin/jsdoc index.ts -R README.md -d doc", + "prepublishOnly": "npm run compile && npm run test", + "compile": "tsc" }, "author": { "name": "Marc Walter", @@ -20,7 +23,9 @@ "license": "MIT", "files": [ "cli.js", + "cli.d.ts", "index.js", + "index.d.ts", "bin" ], "repository": { @@ -35,10 +40,12 @@ "report-dir": "test_coverage" }, "dependencies": { - "debug": "^4.3.3", - "minimatch": "~5.1.2" + "debug": "^4.3.4", + "minimatch": "^9.0.3" }, "devDependencies": { + "@types/debug": "^4.1.8", + "@types/node": "^20.4.0", "chai": "^4.3.6", "chai-as-promised": "^7.1.1", "clone": "^2.1.2", @@ -47,7 +54,8 @@ "memfs": "^3.4.1", "mocha": "^9.2.0", "nyc": "^15.1.0", - "prettier": "~2.8.2" + "prettier": "~2.8.2", + "typescript": "^5" }, "engines": { "node": ">=10.10.0" diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..e743888 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "declaration": true, + "target": "ES2015", + "module": "commonjs", + "moduleResolution": "node" + }, + "include": ["index.ts", "cli.ts"] +} \ No newline at end of file