From 875c3d18dd59f58737c4df64cd95e2a949e2fc33 Mon Sep 17 00:00:00 2001 From: Markus Tacker Date: Thu, 11 Apr 2024 14:00:49 +0200 Subject: [PATCH] feat: add hasFolder() --- .github/workflows/test-and-release.yaml | 1 + .husky/pre-commit | 3 +- package.json | 3 +- src/hashFolder.spec.ts | 15 +++++++ src/hashFolder.ts | 57 +++++++++++++++++++++++++ src/test-folder/nest-folder/another.txt | 1 + src/test-folder/test.txt | 1 + 7 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 src/hashFolder.spec.ts create mode 100644 src/hashFolder.ts create mode 100644 src/test-folder/nest-folder/another.txt create mode 100644 src/test-folder/test.txt diff --git a/.github/workflows/test-and-release.yaml b/.github/workflows/test-and-release.yaml index bf79162..ee50ddd 100644 --- a/.github/workflows/test-and-release.yaml +++ b/.github/workflows/test-and-release.yaml @@ -26,6 +26,7 @@ jobs: run: npx eslint . - name: Check if source code is properly formatted run: npx prettier -c ./ + - run: npm test - name: Semantic release continue-on-error: true run: npx semantic-release diff --git a/.husky/pre-commit b/.husky/pre-commit index d0a7784..8376466 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1 +1,2 @@ -npx lint-staged \ No newline at end of file +npx lint-staged +npm test \ No newline at end of file diff --git a/package.json b/package.json index 12b96d2..2f1905f 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,8 @@ "type": "module", "scripts": { "prepare": "husky", - "prepublishOnly": "npx tsc --noEmit false --outDir ./dist -d" + "prepublishOnly": "npx tsc --noEmit false --outDir ./dist -d", + "test": "npx tsx --test src/*.spec.ts" }, "repository": { "type": "git", diff --git a/src/hashFolder.spec.ts b/src/hashFolder.spec.ts new file mode 100644 index 0000000..62db93f --- /dev/null +++ b/src/hashFolder.spec.ts @@ -0,0 +1,15 @@ +import { describe, it } from 'node:test' +import assert from 'node:assert/strict' +import path from 'node:path' +import { hashFolder } from './hashFolder.js' +import { fileURLToPath } from 'node:url' +const __dirname = fileURLToPath(new URL('.', import.meta.url)) + +void describe('hashFolder', () => { + void it('should calculate correct MD5 hash value for folder with files', async () => { + const folderPath = path.join(__dirname, 'test-folder') + const expectedMd5 = '2446ef9dbaecdce8dbf59fdfb68b0f81' + const actualMd5 = await hashFolder(folderPath) + assert.equal(actualMd5, expectedMd5) + }) +}) diff --git a/src/hashFolder.ts b/src/hashFolder.ts new file mode 100644 index 0000000..fc9b0d8 --- /dev/null +++ b/src/hashFolder.ts @@ -0,0 +1,57 @@ +import { createHash } from 'node:crypto' +import { createReadStream, readdirSync, statSync } from 'node:fs' +import { join } from 'node:path' + +const hashFile = async (file: string): Promise => { + const hash = createHash('md5') + const content = createReadStream(file) + + return new Promise((resolve, reject) => { + content.pipe(hash) + content + .on('close', () => { + return resolve(hash.digest('hex')) + }) + .on('error', (error) => { + return reject(error) + }) + }) +} + +const generateMd5ForFolder = async ( + path: string, +): Promise> => { + const files = readdirSync(path) + const md5Hashes: Record = {} + + for (const file of files) { + const filePath = join(path, file) + const fileStats = statSync(filePath) + + if (fileStats.isFile()) { + md5Hashes[filePath] = await hashFile(filePath) + } else if (fileStats.isDirectory()) { + const filesInNestedFolder = await generateMd5ForFolder(filePath) + for (const file in filesInNestedFolder) { + md5Hashes[file] = filesInNestedFolder[file] ?? '' + } + } + } + + return md5Hashes +} + +export const hashFolder = async (path: string): Promise => { + const filesHash = await generateMd5ForFolder(path) + + const hashMD5 = createHash('md5') + Object.entries(filesHash) + .sort((a, b) => + a[0].toLocaleLowerCase().localeCompare(b[0].toLocaleLowerCase()), + ) + .forEach(([file, hash]) => { + hashMD5.update(`${hash} ${file.replace(path, '')}`) + }) + + return hashMD5.digest('hex') +} diff --git a/src/test-folder/nest-folder/another.txt b/src/test-folder/nest-folder/another.txt new file mode 100644 index 0000000..9af7d33 --- /dev/null +++ b/src/test-folder/nest-folder/another.txt @@ -0,0 +1 @@ +Eget aliquet nibh praesent tristique. Nullam eget felis eget nunc lobortis mattis aliquam faucibus purus. Ut tellus elementum sagittis vitae. Adipiscing enim eu turpis egestas pretium aenean pharetra magna ac. Commodo sed egestas egestas fringilla phasellus faucibus scelerisque eleifend donec. Aliquam sem fringilla ut morbi. Urna id volutpat lacus laoreet. Integer eget aliquet nibh praesent tristique magna sit amet. Mi tempus imperdiet nulla malesuada pellentesque elit eget gravida. Lacinia quis vel eros donec ac odio. Eget gravida cum sociis natoque penatibus et magnis dis parturient. Mauris rhoncus aenean vel elit. Sollicitudin tempor id eu nisl. Risus pretium quam vulputate dignissim suspendisse. Vitae semper quis lectus nulla at. Purus ut faucibus pulvinar elementum integer. Placerat orci nulla pellentesque dignissim enim sit amet. Lectus quam id leo in vitae turpis. \ No newline at end of file diff --git a/src/test-folder/test.txt b/src/test-folder/test.txt new file mode 100644 index 0000000..33a8f28 --- /dev/null +++ b/src/test-folder/test.txt @@ -0,0 +1 @@ +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla scelerisque interdum elit ac ultricies. Proin vitae enim dictum, convallis odio quis, pellentesque turpis. Curabitur blandit magna vel dui consectetur bibendum. Fusce viverra tristique quam at mattis. Suspendisse vehicula libero vitae tincidunt convallis. Nam malesuada lacinia semper. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Sed sit amet volutpat nunc. Donec malesuada dolor ut turpis viverra volutpat. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed elementum malesuada odio, vel vehicula nisi faucibus a. Sed bibendum justo ut lectus malesuada efficitur. Vivamus vitae pharetra justo. \ No newline at end of file