From f29a3bd745fbbaa43bd38a4b075da457ea31a846 Mon Sep 17 00:00:00 2001 From: Haaxor1689 Date: Sat, 9 Dec 2023 11:04:28 +0100 Subject: [PATCH] Better input handling and patchignore documentation --- README.md | 29 +++++++++++++++++++++++++++++ package.json | 2 +- src/archiver.ts | 34 ++++++++++++++++++++-------------- src/index.ts | 23 ++++++++++++++++++++--- src/logger.ts | 12 ------------ src/patchignore.ts | 9 ++++++++- 6 files changed, 78 insertions(+), 31 deletions(-) delete mode 100644 src/logger.ts diff --git a/README.md b/README.md index 5fd21c0..1c00ab5 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,35 @@ haax-mpq - `directoryPath`: Full path to the folder - `archivePath`: Full path to the output MPQ file. Default: `.mpq` +## Patchignore + +You can add a `.patchignore` file to your archive folder that will list rules about what files shouldn't be included in the built archive. It uses a similar syntax to `.gitignore` files (using [anymatch](https://github.com/micromatch/anymatch) library). + +If no `.patchignore` file is found, these defaults will be used: + +``` +# ignore git related stuff +.git/** +.github/** +.gitignore + +# ignore all file formats that have nothing to do in patch +**/*.json +**/*.yml +**/*.yaml +**/*.exe +**/*.dll +**/*.db +**/*.csv +**/*.png +**/*.psd +**/*.txt +**/*.md +**/*.sql + +.patchignore +``` + ## Executable You can also use this CLI as a standalone executable that can be found in Releases. Download `haax-mpq.exe` for Windows or `haax-mpq` for Linux. You can then drag & drop any folder you want to archive onto the executable. diff --git a/package.json b/package.json index 4f4be2e..53db728 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "haax-mpq", - "version": "1.1.3", + "version": "1.1.4", "author": "Haaxor1689", "license": "MIT", "repository": { diff --git a/src/archiver.ts b/src/archiver.ts index f11424a..822686f 100644 --- a/src/archiver.ts +++ b/src/archiver.ts @@ -14,9 +14,18 @@ import { MPQ_CREATE, MPQ_FILE } from 'stormlib-node/dist/enums'; -import Logger, { getTimeElapsed } from './logger'; import Patchignore from './patchignore'; +const pad = (v: number, p = 2) => v.toString().padStart(p, '0'); +export const getTimeElapsed = (startDate: Date, endDate = new Date()) => { + let ms = endDate.getTime() - startDate.getTime(); + const m = Math.floor(ms / (1000 * 60)); + ms %= 1000 * 60; + const s = Math.floor(ms / 1000); + ms %= 1000; + return `${m ? `${pad(m)}:` : ''}${pad(s)}.${pad(ms, 3)}`; +}; + type ArchiveBuildOptions = { archivePath: string; directoryPath: string; @@ -27,12 +36,12 @@ export const build = async (input: ArchiveBuildOptions) => { const matches = await Patchignore(input.directoryPath); - Logger.log(`Building archive "${path.basename(input.archivePath)}"...`); + console.log( + `Building "${input.directoryPath}" into archive "${input.archivePath}"...` + ); const getAllFiles = async (filePath: string): Promise => { - const relativePath = filePath - .slice(input.directoryPath.length + 1) - .replaceAll('\\\\', '/'); + const relativePath = filePath.slice(input.directoryPath.length + 1); if (await matches(relativePath)) return []; if (!(await fs.lstat(filePath)).isDirectory()) return [filePath]; @@ -58,19 +67,16 @@ export const build = async (input: ArchiveBuildOptions) => { try { for (const file of files) { - const fullPath = file.replaceAll('\\\\', '/'); - const relativePath = file - .slice(input.directoryPath.length + 1) - .replaceAll('\\\\', '/'); + const relativePath = file.slice(input.directoryPath.length + 1); if (await matches(relativePath)) { - Logger.log(`Ignored "${relativePath}"`); + console.log(`Ignored "${relativePath}"`); continue; } SFileAddFileEx( hMpq, - fullPath, + file, relativePath, MPQ_FILE.COMPRESS, MPQ_COMPRESSION.ZLIB, @@ -78,16 +84,16 @@ export const build = async (input: ArchiveBuildOptions) => { ); } - Logger.log('Compressing the archive...'); + console.log('Compressing the archive...'); SFileCompactArchive(hMpq); - Logger.log( + console.log( `Archive "${path.basename(input.archivePath)}" built in ${getTimeElapsed( startTime )}s` ); } catch (e) { - Logger.error(e); + console.error(e); } finally { SFileCloseArchive(hMpq); } diff --git a/src/index.ts b/src/index.ts index 39689b1..2a1ed1e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,6 +2,7 @@ import path from 'node:path'; import { build } from './archiver'; +import { existsSync, lstatSync } from 'node:fs'; const args = process.argv.slice(2); if (args.length === 0 || args.length > 2) { @@ -9,8 +10,24 @@ if (args.length === 0 || args.length > 2) { process.exit(1); } -const directoryPath = path.normalize(args[0]); -const archivePath = - args.length === 1 ? `${directoryPath}.mpq` : path.normalize(args[1]); +let directoryPath = path.normalize(args[0]); + +// Handle trailing slash +if (directoryPath.endsWith('\\') || directoryPath.endsWith('/')) { + directoryPath = directoryPath.slice(0, -1); +} + +// Check if directory exists +if (existsSync(directoryPath) && !lstatSync(directoryPath).isDirectory()) { + console.log('First argument must be a directory'); + process.exit(1); +} + +let archivePath = args.length === 1 ? directoryPath : path.normalize(args[1]); + +// Handle extension +if (!archivePath.toLocaleLowerCase().endsWith('.mpq')) { + archivePath = `${directoryPath}.mpq`; +} build({ directoryPath, archivePath }); diff --git a/src/logger.ts b/src/logger.ts deleted file mode 100644 index 93e6a4e..0000000 --- a/src/logger.ts +++ /dev/null @@ -1,12 +0,0 @@ -const pad = (v: number, p = 2) => v.toString().padStart(p, '0'); -export const getTimeElapsed = (startDate: Date, endDate = new Date()) => { - let ms = endDate.getTime() - startDate.getTime(); - const m = Math.floor(ms / (1000 * 60)); - ms %= 1000 * 60; - const s = Math.floor(ms / 1000); - ms %= 1000; - return `${m ? `${pad(m)}:` : ''}${pad(s)}.${pad(ms, 3)}`; -}; - -const Logger = console; -export default Logger; diff --git a/src/patchignore.ts b/src/patchignore.ts index e78db8c..6bbb187 100644 --- a/src/patchignore.ts +++ b/src/patchignore.ts @@ -28,8 +28,15 @@ const defaultPatchignore = ` const Patchignore = async (directoryPath: string) => { const patchignorePath = path.join(directoryPath, '.patchignore'); + const exists = existsSync(patchignorePath); + console.log( + exists + ? 'Loading .patchignore from target directory' + : 'Using default .patchignore' + ); + const patchignore = ( - existsSync(patchignorePath) + exists ? await fs.readFile(patchignorePath, { encoding: 'utf-8' }) : defaultPatchignore )