From d319fd05bc29c1d7b236afae40ffb740d87dbb38 Mon Sep 17 00:00:00 2001 From: techno-express Date: Wed, 23 Dec 2020 13:12:36 -0500 Subject: [PATCH] updates restructoring file order, move util functions to single file --- lib/createArchive.mjs | 18 +- lib/createSfx.mjs | 4 +- lib/deleteArchive.mjs | 16 +- lib/extractArchive.mjs | 17 +- lib/fullArchive.mjs | 17 +- lib/index.d.ts | 82 -------- lib/index.mjs | 125 ++++++++++- lib/listArchive.mjs | 16 +- lib/onlyArchive.mjs | 3 +- lib/renameArchive.mjs | 18 +- lib/testArchive.mjs | 16 +- lib/updateArchive.mjs | 18 +- lib/utility.mjs | 253 +++++++++++++++++++++++ test/lib/utility.test.js | 221 ++++++++++++++++++++ test/util/binary.test.js | 35 ---- test/util/files.test.js | 24 --- test/util/replaceNativeSeparator.test.js | 31 --- test/util/run.test.js | 84 -------- test/util/switches.test.js | 81 -------- util/binary.mjs | 24 --- util/files.mjs | 28 --- util/replaceNativeSeparator.mjs | 14 -- util/run.mjs | 144 ------------- util/switches.mjs | 64 ------ 24 files changed, 606 insertions(+), 747 deletions(-) delete mode 100644 lib/index.d.ts create mode 100644 lib/utility.mjs create mode 100644 test/lib/utility.test.js delete mode 100644 test/util/binary.test.js delete mode 100644 test/util/files.test.js delete mode 100644 test/util/replaceNativeSeparator.test.js delete mode 100644 test/util/run.test.js delete mode 100644 test/util/switches.test.js delete mode 100644 util/binary.mjs delete mode 100644 util/files.mjs delete mode 100644 util/replaceNativeSeparator.mjs delete mode 100644 util/run.mjs delete mode 100644 util/switches.mjs diff --git a/lib/createArchive.mjs b/lib/createArchive.mjs index 5637f54..3756b26 100644 --- a/lib/createArchive.mjs +++ b/lib/createArchive.mjs @@ -1,24 +1,8 @@ 'use strict'; import when from 'when'; -import Files from '../util/files.mjs'; -import ReplaceNativeSeparator from '../util/replaceNativeSeparator.mjs'; -import Run from '../util/run.mjs'; +import { Files, ReplaceNativeSeparator, Run } from './utility.mjs'; -/** - * Create/add content to an archive. - * - * @param filepath {string} Path to the archive. - * @param files {string|array} Files to add. - * @param options {Object} An object of acceptable 7-zip switch options. - * @param override {boolean} should binary directory change? - * - * @resolve {array} Arguments passed to the child-process. - * @progress {array} Listed files and directories. - * @reject {Error} The error as issued by 7-Zip. - * - * @returns {Promise} Promise - */ export default function (filepath, files, options, override = false) { return when.promise(function (resolve, reject, progress) { /** diff --git a/lib/createSfx.mjs b/lib/createSfx.mjs index a8bfaef..f4fe6aa 100644 --- a/lib/createSfx.mjs +++ b/lib/createSfx.mjs @@ -11,7 +11,7 @@ import { join } from 'path'; import fs from 'fs-extra'; -import binary from '../util/binary.mjs'; +import { Binary } from './utility.mjs'; const platformTitle = { win32: 'Windows OS', @@ -133,7 +133,7 @@ export default function ( let SfxDirectory = join(directory, 'SfxPackages'); fs.ensureDirSync(SfxDirectory); let override = ((process.platform == 'win32') && (platform == 'linux' || platform == 'darwin')); - let binaryDirectory = binary(override); + let binaryDirectory = Binary(override); let configFile = join(binaryDirectory.path, 'config.txt'); //let configFile = join(SfxDirectory, 'config.txt'); let config = fs.createWriteStream(configFile, { diff --git a/lib/deleteArchive.mjs b/lib/deleteArchive.mjs index b108832..ae58fb5 100644 --- a/lib/deleteArchive.mjs +++ b/lib/deleteArchive.mjs @@ -1,21 +1,7 @@ 'use strict'; -import Files from '../util/files.mjs'; -import Run from '../util/run.mjs'; +import { Files, Run } from './utility.mjs'; -/** - * Delete content from an archive. - * - * @param filepath {string} Path to the archive. - * @param files {string|array} Files to remove. - * @param options {Object} An object of acceptable 7-zip switch options. - * @param override {boolean} should binary directory change? - * - * @resolve {array} Arguments passed to the child-process. - * @reject {Error} The error as issued by 7-Zip. - * - * @returns {Promise} Promise - */ export default function (filepath, files, options, override = false) { return new Promise(function (resolve, reject) { diff --git a/lib/extractArchive.mjs b/lib/extractArchive.mjs index 5844619..f4edaf4 100644 --- a/lib/extractArchive.mjs +++ b/lib/extractArchive.mjs @@ -1,23 +1,8 @@ 'use strict'; import when from 'when'; -import ReplaceNativeSeparator from '../util/replaceNativeSeparator.mjs'; -import Run from '../util/run.mjs'; +import { ReplaceNativeSeparator, Run } from './utility.mjs'; -/** - * Extract an archive. - * - * @param {string} archive Path to the archive. - * @param {string} dest Destination. - * @param options {Object} An object of acceptable 7-zip switch options. - * @param override {boolean} should binary directory change? - * - * @resolve {array} Arguments passed to the child-process. - * @progress {array} Extracted files and directories. - * @reject {Error} The error as issued by 7-Zip. - * - * @returns {Promise} Promise - */ export default function (filepath, dest = '*', options = {}, override = false) { return when.promise(function (resolve, reject, progress) { /** diff --git a/lib/fullArchive.mjs b/lib/fullArchive.mjs index e9967c1..4cbcdeb 100644 --- a/lib/fullArchive.mjs +++ b/lib/fullArchive.mjs @@ -1,23 +1,8 @@ 'use strict'; import when from 'when'; -import ReplaceNativeSeparator from '../util/replaceNativeSeparator.mjs'; -import Run from '../util/run.mjs'; +import { ReplaceNativeSeparator, Run } from './utility.mjs'; -/** - * Extract an archive with full paths. - * - * @param filepath {string} Path to the archive. - * @param dest {string} Destination. - * @param options {Object} An object of acceptable 7-zip switch options. - * @param override {boolean} should binary directory change? - * - * @resolve {array} Arguments passed to the child-process. - * @progress {array} Extracted files and directories. - * @reject {Error} The error as issued by 7-Zip. - * - * @returns {Promise} Promise - */ export default function (filepath, dest = '*', options = {}, override = false) { return when.promise(function (resolve, reject, progress) { /** diff --git a/lib/index.d.ts b/lib/index.d.ts deleted file mode 100644 index 85a6705..0000000 --- a/lib/index.d.ts +++ /dev/null @@ -1,82 +0,0 @@ -declare module 'node-7z-archive'; -declare class SevenZip { - constructor() { } -} - -export = SevenZip; - -/** - * Create/add content to an archive. - * - * @param filepath {string} Path to the archive. - * @param files {string|array} Files to add. - * @param options {Object} An object of acceptable 7-zip switch options. - * @param override {boolean} should binary directory change? - * - * @resolve {array} Arguments passed to the child-process. - * @progress {array} Listed files and directories. - * @reject {Error} The error as issued by 7-Zip. - * - * @returns {Promise} Promise - */ -export function createArchive(filepath: string, files: string | array, options?: object, useBinary?: string, override?: boolean): Promise; - -/** - * Delete content from an archive. - * - * @param filepath {string} Path to the archive. - * @param files {string|array} Files to remove. - * @param options {Object} An object of acceptable 7-zip switch options. - * @param override {boolean} should binary directory change? - * - * @resolve {array} Arguments passed to the child-process. - * @reject {Error} The error as issued by 7-Zip. - * - * @returns {Promise} Promise - */ -export function deleteArchive(filepath: string, files: string | array, options?: object, override?: boolean): Promise; - -/** - * Extract an archive. - * - * @param {string} filepath Path to the archive. - * @param {string} dest Destination. - * @param options {Object} An object of acceptable 7-zip switch options. - * @param override {boolean} should binary directory change? - * - * @resolve {array} Arguments passed to the child-process. - * @progress {array} Extracted files and directories. - * @reject {Error} The error as issued by 7-Zip. - * - * @returns {Promise} Promise - */ -export function extractArchive(filepath: string, dest: string, options?: object, override?: boolean): Promise; - -/** - * Extract only selected files from archive. - * - * @param {string} filepath Path to the archive. - * @param {string} dest Destination. - * @param {string|array} files Files in archive to extract. - * @param options {Object} An object of acceptable 7-zip switch options. - * @param override {boolean} should binary directory change? - * - * @resolve {array} Arguments passed to the child-process. - * @progress {array} Extracted files and directories. - * @reject {Error} The error as issued by 7-Zip. - * - * @returns {Promise} Promise - */ -export function onlyArchive(filepath: string, dest: string, files: string | array, options?: object, override?: boolean): Promise; - -export function fullArchive(filepath: string, files: string | array, options?: object, useBinary?: string, override?: boolean): Promise; -export function listArchive(filepath: string, files: string | array, options?: object, useBinary?: string, override?: boolean): Promise; -export function renameArchive(filepath: string, files: string | array, options?: object, useBinary?: string, override?: boolean): Promise; -export function testArchive(filepath: string, files: string | array, options?: object, useBinary?: string, override?: boolean): Promise; -export function updateArchive(filepath: string, files: string | array, options?: object, useBinary?: string, override?: boolean): Promise; - -export function createWindowsSfx(name: string, files: string | array, destination?: string, options?: object, type?: string): Promise; - -export function createLinuxSfx(name: string, files: string | array, destination?: string, options?: object): Promise; - -export function createMacSfx(name: string, files: string | array, destination?: string, options?: object): Promise; diff --git a/lib/index.mjs b/lib/index.mjs index 6e3fd82..fd4acf5 100644 --- a/lib/index.mjs +++ b/lib/index.mjs @@ -11,15 +11,84 @@ import _testArchive from './testArchive.mjs'; import _updateArchive from './updateArchive.mjs'; import createSfx from './createSfx.mjs'; -class SevenZip { - constructor() { } -} +function SevenZip() { }; -export default SevenZip; +/** + * Create/add content to an archive. + * + * @param filepath {string} Path to the archive. + * @param files {string|array} Files to add. + * @param options {Object} An object of acceptable 7-zip switch options. + * @param override {boolean} should binary directory change? + * + * @resolve {array} Arguments passed to the child-process. + * @progress {array} Listed files and directories. + * @reject {Error} The error as issued by 7-Zip. + * + * @returns {Promise} Promise + */ export const createArchive = SevenZip.createArchive = _createArchive; + +/** + * Delete content from an archive. + * + * @param filepath {string} Path to the archive. + * @param files {string|array} Files to remove. + * @param options {Object} An object of acceptable 7-zip switch options. + * @param override {boolean} should binary directory change? + * + * @resolve {array} Arguments passed to the child-process. + * @reject {Error} The error as issued by 7-Zip. + * + * @returns {Promise} Promise + */ export const deleteArchive = SevenZip.deleteArchive = _deleteArchive; + +/** + * Extract an archive. + * + * @param {string} archive Path to the archive. + * @param {string} dest Destination. + * @param options {Object} An object of acceptable 7-zip switch options. + * @param override {boolean} should binary directory change? + * + * @resolve {array} Arguments passed to the child-process. + * @progress {array} Extracted files and directories. + * @reject {Error} The error as issued by 7-Zip. + * + * @returns {Promise} Promise + */ export const extractArchive = SevenZip.extractArchive = _extractArchive; + +/** + * Extract an archive with full paths. + * + * @param filepath {string} Path to the archive. + * @param dest {string} Destination. + * @param options {Object} An object of acceptable 7-zip switch options. + * @param override {boolean} should binary directory change? + * + * @resolve {array} Arguments passed to the child-process. + * @progress {array} Extracted files and directories. + * @reject {Error} The error as issued by 7-Zip. + * + * @returns {Promise} Promise + */ export const fullArchive = SevenZip.fullArchive = _fullArchive; + +/** + * List contents of archive. + * + * @param filepath {string} Path to the archive. + * @param options {Object} An object of acceptable 7-zip switch options. + * @param override {boolean} should binary directory change? + * + * @progress {array} Listed files and directories. + * @resolve {Object} Tech spec about the archive. + * @reject {Error} The error as issued by 7-Zip. + * + * @returns {Promise} Promise + */ export const listArchive = SevenZip.listArchive = _listArchive; /** @@ -38,8 +107,52 @@ export const listArchive = SevenZip.listArchive = _listArchive; * @returns {Promise} Promise */ export const onlyArchive = SevenZip.onlyArchive = _onlyArchive; + +/** + * Renames files in archive. + * + * @param filepath {string} Path to the archive. + * @param files {string} Files pairs to rename in archive. + * @param options {Object} An object of acceptable 7-zip switch options. + * @param override {boolean} should binary directory change? + * + * @resolve {array} Arguments passed to the child-process. + * @progress {array} Listed files and directories. + * @reject {Error} The error as issued by 7-Zip. + * + * @returns {Promise} Promise + */ export const renameArchive = SevenZip.renameArchive = _renameArchive; + +/** + * Test integrity of archive. + * + * @param filepath {string} Path to the archive. + * @param options {Object} An object of acceptable 7-zip switch options. + * @param override {boolean} should binary directory change? + * + * @resolve {array} Arguments passed to the child-process. + * @progress {array} Extracted files and directories. + * @reject {Error} The error as issued by 7-Zip. + * + * @returns {Promise} Promise + */ export const testArchive = SevenZip.testArchive = _testArchive; + +/** + * Update content to an archive. + * + * @param filepath {string} Path to the archive. + * @param files {string} Files to update. + * @param options {Object} An object of acceptable 7-zip switch options. + * @param override {boolean} should binary directory change? + * + * @resolve {array} Arguments passed to the child-process. + * @progress {array} Listed files and directories. + * @reject {Error} The error as issued by 7-Zip. + * + * @returns {Promise} Promise + */ export const updateArchive = SevenZip.updateArchive = _updateArchive; export const createSfxWindows = SevenZip.windowsSfx = function (name, files, destination, options, type) { @@ -53,3 +166,7 @@ export const createSfxLinux = SevenZip.linuxSfx = function (name, files, destina export const createSfxMac = SevenZip.macSfx = function (name, files, destination, options) { return createSfx(name, files, destination, options, 'console', 'darwin', '.pkg'); }; + +export default SevenZip; + +export const Zip = SevenZip; diff --git a/lib/listArchive.mjs b/lib/listArchive.mjs index 043b5ea..fedb8bc 100644 --- a/lib/listArchive.mjs +++ b/lib/listArchive.mjs @@ -1,22 +1,8 @@ 'use strict'; import when from 'when'; -import ReplaceNativeSeparator from '../util/replaceNativeSeparator.mjs'; -import Run from '../util/run.mjs'; +import { ReplaceNativeSeparator, Run } from './utility.mjs'; -/** - * List contents of archive. - * - * @param filepath {string} Path to the archive. - * @param options {Object} An object of acceptable 7-zip switch options. - * @param override {boolean} should binary directory change? - * - * @progress {array} Listed files and directories. - * @resolve {Object} Tech spec about the archive. - * @reject {Error} The error as issued by 7-Zip. - * - * @returns {Promise} Promise - */ export default function (filepath, options, override = false) { return when.promise(function (resolve, reject, progress) { let spec = {}; diff --git a/lib/onlyArchive.mjs b/lib/onlyArchive.mjs index d3eee7a..81bee8e 100644 --- a/lib/onlyArchive.mjs +++ b/lib/onlyArchive.mjs @@ -1,8 +1,7 @@ 'use strict'; import when from 'when'; -import ReplaceNativeSeparator from '../util/replaceNativeSeparator.mjs'; -import Run from '../util/run.mjs'; +import { ReplaceNativeSeparator, Run } from './utility.mjs'; /** * Extract only selected files from archive. diff --git a/lib/renameArchive.mjs b/lib/renameArchive.mjs index 8ed6789..70d2b57 100644 --- a/lib/renameArchive.mjs +++ b/lib/renameArchive.mjs @@ -1,24 +1,8 @@ 'use strict'; import when from 'when'; -import Files from '../util/files.mjs'; -import ReplaceNativeSeparator from '../util/replaceNativeSeparator.mjs'; -import Run from '../util/run.mjs'; +import { Files, ReplaceNativeSeparator, Run } from './utility.mjs'; -/** - * Renames files in archive. - * - * @param filepath {string} Path to the archive. - * @param files {string} Files pairs to rename in archive. - * @param options {Object} An object of acceptable 7-zip switch options. - * @param override {boolean} should binary directory change? - * - * @resolve {array} Arguments passed to the child-process. - * @progress {array} Listed files and directories. - * @reject {Error} The error as issued by 7-Zip. - * - * @returns {Promise} Promise - */ export default function (filepath, files, options, override = false) { return when.promise(function (resolve, reject, progress) { /** diff --git a/lib/testArchive.mjs b/lib/testArchive.mjs index e605728..c0f8642 100644 --- a/lib/testArchive.mjs +++ b/lib/testArchive.mjs @@ -1,22 +1,8 @@ 'use strict'; import when from 'when'; -import ReplaceNativeSeparator from '../util/replaceNativeSeparator.mjs'; -import Run from '../util/run.mjs'; +import { ReplaceNativeSeparator, Run } from './utility.mjs'; -/** - * Test integrity of archive. - * - * @param filepath {string} Path to the archive. - * @param options {Object} An object of acceptable 7-zip switch options. - * @param override {boolean} should binary directory change? - * - * @resolve {array} Arguments passed to the child-process. - * @progress {array} Extracted files and directories. - * @reject {Error} The error as issued by 7-Zip. - * - * @returns {Promise} Promise - */ export default function (filepath, options, override = false) { return when.promise(function (resolve, reject, progress) { /** diff --git a/lib/updateArchive.mjs b/lib/updateArchive.mjs index 2047738..7ad0577 100644 --- a/lib/updateArchive.mjs +++ b/lib/updateArchive.mjs @@ -1,24 +1,8 @@ 'use strict'; import when from 'when'; -import Files from '../util/files.mjs'; -import ReplaceNativeSeparator from '../util/replaceNativeSeparator.mjs'; -import Run from '../util/run.mjs'; +import { Files, ReplaceNativeSeparator, Run } from './utility.mjs'; -/** - * Update content to an archive. - * - * @param filepath {string} Path to the archive. - * @param files {string} Files to update. - * @param options {Object} An object of acceptable 7-zip switch options. - * @param override {boolean} should binary directory change? - * - * @resolve {array} Arguments passed to the child-process. - * @progress {array} Listed files and directories. - * @reject {Error} The error as issued by 7-Zip. - * - * @returns {Promise} Promise - */ export default function (filepath, files, options, override = false) { return when.promise(function (resolve, reject, progress) { /** diff --git a/lib/utility.mjs b/lib/utility.mjs new file mode 100644 index 0000000..d9fecd9 --- /dev/null +++ b/lib/utility.mjs @@ -0,0 +1,253 @@ +'use strict'; + +import when from 'when'; +import { EOL } from 'os'; +import { fileURLToPath } from 'url'; +import { dirname, join, sep, sep as nativeSeparator, normalize } from 'path'; +import { spawning, isUndefined, isArray, isString } from 'node-sys'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +export const Binary = function (override = false, binary = '7z') { + let path = join(__dirname, '..', "binaries", (override == true ? process.platform + sep + 'other32' : process.platform)); + let filename = (process.platform == "win32") ? binary + '.exe' : binary; + return { + path: path, + filename: filename, + filepath: join(path, filename) + } +}; + +/** + * Transform a list of files that can be an array or a string into a string + * that can be passed to the `run` function as part of the `command` parameter. + * @param {string|array} files + * @return {string} + */ +export const Files = function (files) { + + if (isUndefined(files)) { + return ''; + } + + let toProcess = ''; + if (isArray(files)) { + files.forEach(function (f) { + toProcess += '"' + f + '" '; + }); + toProcess = toProcess.trim(); + } else { + toProcess = '"' + files + '"'; + } + + return toProcess; +}; + +/** + * @param {string} path A path with the native directory separator. + * @return {string} A path with / for directory separator. + */ +export const ReplaceNativeSeparator = function (path) { + let result = path, next; + while ((next = result.replace(nativeSeparator, '/')) !== result) { + result = next; + } + + return result; +}; + +/** + * @param {string} binary which binary to use. + * @param {string} command The command to run. + * @param {Array} switches Options for 7-Zip as an array. + * @param {boolean} override should binary directory change? + * + * @progress {string} stdout message. + * @reject {Error} The error issued by 7-Zip. + * @reject {number} Exit code issued by 7-Zip. + * + * @returns {Promise} Promise + */ +export const Run = function (binary = '7z', command = null, switches = {}, override = false) { + return when.promise(function (fulfill, reject, progress) { + + // Parse the command variable. If the command is not a string reject the + // Promise. Otherwise transform the command into two variables: the command + // name and the arguments. + if (typeof command !== 'string' || typeof binary !== 'string') { + return reject(new Error('Command and Binary must be a string')); + } + + // add platform binary to command + let sevenBinary = Binary(override, binary); + let cmd = sevenBinary.filepath; + let args = [command.split(' ')[0]]; + + // Parse and add command (non-switches parameters) to `args`. + let regexpCommands = /"((?:\\.|[^"\\])*)"/g; + let commands = command.match(regexpCommands); + if (commands) { + commands.forEach(function (c) { + c = c.replace(/\//g, sep); + c = c.replace(/\\/g, sep); + c = normalize(c); + args.push(c); + }); + } + + // Special treatment for the output switch because it is exposed as a + // parameter in the API and not as a option. Plus wildcards can be passed. + let regexpOutput = /-o"((?:\\.|[^"\\])*)"/g; + let output = command.match(regexpOutput); + if (output) { + args.pop(); + let o = output[0]; + o = o.replace(/\//g, sep); + o = o.replace(/\\/g, sep); + o = o.replace(/"/g, ''); + o = normalize(o); + args.push(o); + } + + if (switches.files) { + let files = switches.files; + delete switches.files; + if (isArray(files)) { + files.forEach(function (s) { + args.push(s); + }); + } else { + args.push(files); + } + + args.push('-r'); + args.push('-aoa'); + } + + // Add switches to the `args` array. + let switchesArray = Switches(switches); + switchesArray.forEach(function (s) { + args.push(s); + }); + + // Remove now double quotes. If present in the spawned process 7-Zip will + // read them as part of the paths (e.g.: create a `"archive.7z"` with + // quotes in the file-name); + args.forEach(function (e, i) { + if (!isString(e)) { + return; + } + if (e.substr(0, 1) !== '-') { + e = e.replace(/^"/, ''); + e = e.replace(/"$/, ''); + args[i] = e; + } + }); + + // Add bb2 to args array so we get file info + args.push('-bb2'); + + // When an stdout is emitted, parse it. If an error is detected in the body + // of the stdout create an new error with the 7-Zip error message as the + // error's message. Otherwise progress with stdout message. + let err; + let reg = new RegExp('Error:(' + EOL + '|)?(.*)', 'i'); + let onprogress = (object) => { + progress(object.output); + return args; + }; + + let onerror = (data) => { + let res = reg.exec(data); + if (res) { + err = new Error(res[2].substr(0, res[2].length - 1)); + return err; + } + }; + + let res = { + cmd: cmd, + args: args, + options: { + stdio: 'pipe', + onprogress: onprogress, + onerror: onerror + } + }; + + spawning(res.cmd, res.args, res.options) + .then((data) => { + if (data === args) + return fulfill(args); + + return reject(err); + }) + .catch((err) => { + return reject(err); + }); + }); +}; + + +/** + * Transform an object of options into an array that can be passed to the + * spawned child process. + * @param {Object} switches An object of options + * @return {array} Array to pass to the `run` function. + */ +export const Switches = function (switches) { + + // Default value for switches + switches = switches || {}; + + var a = []; + // Set default values of boolean switches + switches.so = (switches.so === true) ? true : false; + switches.spl = (switches.spl === true) ? true : false; + switches.ssc = (switches.ssc === false) ? false : true; + switches.ssw = (switches.ssw === true) ? true : false; + switches.y = (switches.y === false) ? false : true; + + var s; + /*jshint forin:false*/ + for (s in switches) { + + // Switches that are set or not. Just add them to the array if they are + // present. Differ the `ssc` switch treatment to later in the function. + if (switches[s] === true && s !== 'ssc') { + a.push('-' + s); + } + + // Switches with a value. Detect if the value contains a space. If it does + // wrap the value with double quotes. Else just add the switch and its value + // to the string. Doubles quotes are used for parsing with a RegExp later. + if (typeof switches[s] !== 'boolean') { + + // Special treatment for wildcards + if (s === 'wildcards') { + a.unshift(switches.wildcards); + } + + // Allow raw switches to be added to the command, repeating switches like + // -i is not possible otherwise. + else if (s === 'raw') { + switches.raw.forEach(function (rawValue) { + a.push(rawValue); + }); + } else if (switches[s].indexOf(' ') === -1) { + a.push('-' + s + switches[s]); + } else { + a.push('-' + s + '"' + switches[s] + '"'); + } + } + + // Special treatment for `-ssc` + if (s === 'ssc') { + a.push((switches.ssc === true) ? '-ssc' : '-ssc-'); + } + + } + + return a; +}; diff --git a/test/lib/utility.test.js b/test/lib/utility.test.js new file mode 100644 index 0000000..7bb1164 --- /dev/null +++ b/test/lib/utility.test.js @@ -0,0 +1,221 @@ +/*global describe, it */ +'use strict'; + +import chai from 'chai'; +import { Binary, Files, ReplaceNativeSeparator, Run, Switches } from '../../lib/utility.mjs'; +import { sep } from 'path'; +const expect = chai.expect; + +describe('Method: `Binary`', function () { + let _7zCmd = Binary(); + it('should return an object', function (done) { + expect(_7zCmd).to.be.an('object'); + done(); + }); + + it('should return an object with key fields of `path, filename, filepath` and be string', function (done) { + expect(_7zCmd).to.have.property('path'); + expect(_7zCmd).to.have.property('filename'); + expect(_7zCmd).to.have.property('filepath'); + expect(_7zCmd.filepath).to.be.a('string'); + done(); + }); + + it('should return an path with `other32` included', function (done) { + let _7zPath = Binary(true); + expect(_7zPath.path.includes('other32')).to.be.true; + done(); + }); + + it('should return an binary with `7za` included', function (done) { + let _7zPath = Binary(true, '7za'); + expect(_7zPath.filename.includes('7za')).to.be.true; + done(); + }); +}); + +describe('Utility: `Files`', function () { + it('should error on invalid files', function () { + var r = Files(); + expect(r).to.eql(''); + }); + + it('should works with strings', function () { + var r = Files('hello test'); + expect(r).to.eql('"hello test"'); + }); + + it('should works with arrays', function () { + var r = Files(['hello test', 'hello world']); + expect(r).to.eql('"hello test" "hello world"'); + }); +}); + +describe('Utility: `ReplaceNativeSeparator`', function () { + it('should replace the native directory separator (' + sep + ')' + + ' with / and allows / in input', + function (done) { + [ + ['abc', 'abc'], + ['å/Ö', 'å/Ö'], + ['3' + sep + 'π' + sep + '1' + sep + '4.txt', '3/π/1/4.txt'], + ['abc/def' + sep + 'g', 'abc/def/g'], + ['directory' + sep + 'file', 'directory/file'], + ['a' + sep + 'b' + sep + 'c' + sep + 'd.txt', 'a/b/c/d.txt'], + ] + .forEach(function (inputAndExpectedOutput) { + var input = inputAndExpectedOutput[0]; + var expectedOutput = inputAndExpectedOutput[1]; + expect(ReplaceNativeSeparator(input)).to.eql(expectedOutput); + }); + done(); + }); +}); + +describe('Utility: `Run`', function () { + it('should return an error with invalid command type', function (done) { + Run(0).catch(function (err) { + expect(err.message).to.eql('Command and Binary must be a string'); + done(); + }); + }); + + it('should return an error on when 7z gets one', function (done) { + Run('7z', "???").catch(function (err) { + expect(err.message).to.eql('Unsupported command'); + done(); + }); + }); + + it('should return an stdout on progress', function (done) { + Run('7z', '', { + h: true + }) + .progress(function (data) { + expect(data).to.be.a('string'); + }) + .then(function () { + done(); + }); + }); + + it('should correctly parse complex commands', function (done) { + Run('7za', 'a ".tmp/test/archive.7z" "*.exe" "*.dll"', { + m0: '=BCJ', + m1: '=LZMA:d=21' + }) + .then(function (res) { + expect(res).to.contain('a'); + expect(res).to.contain('.tmp' + sep + 'test' + sep + 'archive.7z'); + expect(res).to.contain('*.exe'); + expect(res).to.contain('*.dll'); + expect(res).to.contain('-m0=BCJ'); + expect(res).to.contain('-m1=LZMA:d=21'); + expect(res).to.contain('-ssc'); + expect(res).to.contain('-y'); + done(); + }); + }); + + it('should correctly parse complex commands with spaces', function (done) { + Run('7za', 'a ".tmp/Folder A/Folder B\\archive.7z" "*.exe" "*.dll"', { + m0: '=BCJ', + m1: '=LZMA:d=21', + p: 'My mhjls/\\c $^é5°', + }) + .then(function (res) { + expect(res).to.contain('a'); + /*jshint maxlen:false*/ + expect(res).to.contain('.tmp' + sep + 'Folder A' + sep + 'Folder B' + sep + 'archive.7z'); + expect(res).to.contain('*.exe'); + expect(res).to.contain('*.dll'); + expect(res).to.contain('-m0=BCJ'); + expect(res).to.contain('-m1=LZMA:d=21'); + expect(res).to.contain('-p"My mhjls/\\c $^é5°"'); + expect(res).to.contain('-ssc'); + expect(res).to.contain('-y'); + done(); + }); + }); + + it('should handle error when the command could not be found', function (done) { + Run('7zxxx', 'a ".tmp/test/archive.7z" "*.exe" "*.dll"').catch(function (err) { + expect(err.message).to.contain('ENOENT'); + done(); + }); + }); +}); + +describe('Utility: `Switches`', function () { + it('should return default flags with no args', function () { + expect(Switches({})).to.contain('-ssc'); + expect(Switches({})).to.contain('-y'); + }); + + it('should return -ssc with flag { ssc: true }', function () { + expect(Switches({ + ssc: true + })).to.contain('-ssc'); + expect(Switches({ + ssc: true + })).to.contain('-y'); + }); + + it('should return -ssc- with flag { ssc: false }', function () { + expect(Switches({ + ssc: false + })).to.contain('-ssc-'); + }); + + it('should return non default booleans when specified', function () { + var r = Switches({ + so: true, + spl: true, + ssw: true, + y: false + }); + expect(r).to.contain('-so'); + expect(r).to.contain('-spl'); + expect(r).to.contain('-ssc'); + expect(r).to.contain('-ssw'); + expect(r).not.to.contain('-y'); + }); + + it('should return complex values when needed', function () { + var r = Switches({ + ssc: true, + ssw: true, + mx0: true + }); + expect(r).to.contain('-ssc'); + expect(r).to.contain('-ssw'); + expect(r).to.contain('-mx0'); + expect(r).to.contain('-y'); + }); + + it('should return complex values with spaces and quotes', function () { + var r = Switches({ + ssc: true, + ssw: true, + m0: '=BCJ', + m1: '=LZMA:d=21', + p: 'My Super Pasw,àù£*', + sfx: '7zSD.sfx', + }); + expect(r).to.contain('-ssc'); + expect(r).to.contain('-ssw'); + expect(r).to.contain('-m0=BCJ'); + expect(r).to.contain('-m1=LZMA:d=21'); + expect(r).to.contain('-p"My Super Pasw,àù£*"'); + expect(r).to.contain('-y'); + expect(r).to.contain('-sfx7zSD.sfx'); + }); + + it('should works with the `raw` switch', function () { + var r = Switches({ + raw: ['-i!*.jpg', '-i!*.png'], + }); + expect(r).to.contain('-i!*.jpg'); + expect(r).to.contain('-i!*.png'); + }); +}); diff --git a/test/util/binary.test.js b/test/util/binary.test.js deleted file mode 100644 index 5a2b4e6..0000000 --- a/test/util/binary.test.js +++ /dev/null @@ -1,35 +0,0 @@ -/*global describe, it */ -'use strict'; -import chai from 'chai'; -import binary from '../../util/binary.mjs'; -const expect = chai.expect; - -describe('Method: `binary`', function () { - let _7zCmd = binary(); - - it('should return an object', function (done) { - expect(_7zCmd).to.be.an('object'); - done(); - }); - - it('should return an object with key fields of `path, filename, filepath` and be string', function (done) { - expect(_7zCmd).to.have.property('path'); - expect(_7zCmd).to.have.property('filename'); - expect(_7zCmd).to.have.property('filepath'); - expect(_7zCmd.filepath).to.be.a('string'); - done(); - }); - - it('should return an path with `other32` included', function (done) { - let _7zPath = binary(true); - expect(_7zPath.path.includes('other32')).to.be.true; - done(); - }); - - it('should return an binary with `7za` included', function (done) { - let _7zPath = binary(true, '7za'); - expect(_7zPath.filename.includes('7za')).to.be.true; - done(); - }); - -}); diff --git a/test/util/files.test.js b/test/util/files.test.js deleted file mode 100644 index 2677043..0000000 --- a/test/util/files.test.js +++ /dev/null @@ -1,24 +0,0 @@ -/*global describe, it */ -'use strict'; -import chai from 'chai'; -import files from '../../util/files.mjs'; -const expect = chai.expect; - -describe('Utility: `files`', function () { - - it('should error on invalid files', function () { - var r = files(); - expect(r).to.eql(''); - }); - - it('should works with strings', function () { - var r = files('hello test'); - expect(r).to.eql('"hello test"'); - }); - - it('should works with arrays', function () { - var r = files(['hello test', 'hello world']); - expect(r).to.eql('"hello test" "hello world"'); - }); - -}); diff --git a/test/util/replaceNativeSeparator.test.js b/test/util/replaceNativeSeparator.test.js deleted file mode 100644 index adf10b2..0000000 --- a/test/util/replaceNativeSeparator.test.js +++ /dev/null @@ -1,31 +0,0 @@ -/*global describe, it */ -'use strict'; -import chai from 'chai'; -import replaceNativeSeparator from '../../util/replaceNativeSeparator.mjs'; -import { - sep -} from 'path'; -const expect = chai.expect; - -describe('Utility: `replaceNativeSeparator`', function () { - - it('should replace the native directory separator (' + sep + ')' + - ' with / and allows / in input', - function (done) { - [ - ['abc', 'abc'], - ['å/Ö', 'å/Ö'], - ['3' + sep + 'π' + sep + '1' + sep + '4.txt', '3/π/1/4.txt'], - ['abc/def' + sep + 'g', 'abc/def/g'], - ['directory' + sep + 'file', 'directory/file'], - ['a' + sep + 'b' + sep + 'c' + sep + 'd.txt', 'a/b/c/d.txt'], - ] - .forEach(function (inputAndExpectedOutput) { - var input = inputAndExpectedOutput[0]; - var expectedOutput = inputAndExpectedOutput[1]; - expect(replaceNativeSeparator(input)).to.eql(expectedOutput); - }); - done(); - }); - -}); diff --git a/test/util/run.test.js b/test/util/run.test.js deleted file mode 100644 index d759d77..0000000 --- a/test/util/run.test.js +++ /dev/null @@ -1,84 +0,0 @@ -/*global describe, it */ -'use strict'; -import chai from 'chai'; -import run from '../../util/run.mjs'; -import { - sep -} from 'path'; -const expect = chai.expect; - -describe('Utility: `run`', function () { - - it('should return an error with invalid command type', function (done) { - run(0).catch(function (err) { - expect(err.message).to.eql('Command and Binary must be a string'); - done(); - }); - }); - - it('should return an error on when 7z gets one', function (done) { - run('7z', "???").catch(function (err) { - expect(err.message).to.eql('Unsupported command'); - done(); - }); - }); - - it('should return an stdout on progress', function (done) { - run('7z', '', { - h: true - }) - .progress(function (data) { - expect(data).to.be.a('string'); - }) - .then(function () { - done(); - }); - }); - - it('should correctly parse complex commands', function (done) { - run('7za', 'a ".tmp/test/archive.7z" "*.exe" "*.dll"', { - m0: '=BCJ', - m1: '=LZMA:d=21' - }) - .then(function (res) { - expect(res).to.contain('a'); - expect(res).to.contain('.tmp' + sep + 'test' + sep + 'archive.7z'); - expect(res).to.contain('*.exe'); - expect(res).to.contain('*.dll'); - expect(res).to.contain('-m0=BCJ'); - expect(res).to.contain('-m1=LZMA:d=21'); - expect(res).to.contain('-ssc'); - expect(res).to.contain('-y'); - done(); - }); - }); - - it('should correctly parse complex commands with spaces', function (done) { - run('7za', 'a ".tmp/Folder A/Folder B\\archive.7z" "*.exe" "*.dll"', { - m0: '=BCJ', - m1: '=LZMA:d=21', - p: 'My mhjls/\\c $^é5°', - }) - .then(function (res) { - expect(res).to.contain('a'); - /*jshint maxlen:false*/ - expect(res).to.contain('.tmp' + sep + 'Folder A' + sep + 'Folder B' + sep + 'archive.7z'); - expect(res).to.contain('*.exe'); - expect(res).to.contain('*.dll'); - expect(res).to.contain('-m0=BCJ'); - expect(res).to.contain('-m1=LZMA:d=21'); - expect(res).to.contain('-p"My mhjls/\\c $^é5°"'); - expect(res).to.contain('-ssc'); - expect(res).to.contain('-y'); - done(); - }); - }); - - it('should handle error when the command could not be found', function (done) { - run('7zxxx', 'a ".tmp/test/archive.7z" "*.exe" "*.dll"').catch(function (err) { - expect(err.message).to.contain('ENOENT'); - done(); - }); - }); - -}); diff --git a/test/util/switches.test.js b/test/util/switches.test.js deleted file mode 100644 index 7700e0f..0000000 --- a/test/util/switches.test.js +++ /dev/null @@ -1,81 +0,0 @@ -/*global describe, it */ -'use strict'; -import chai from 'chai'; -import switches from '../../util/switches.mjs'; -const expect = chai.expect; - -describe('Utility: `switches`', function () { - - it('should return default flags with no args', function () { - expect(switches({})).to.contain('-ssc'); - expect(switches({})).to.contain('-y'); - }); - - it('should return -ssc with flag { ssc: true }', function () { - expect(switches({ - ssc: true - })).to.contain('-ssc'); - expect(switches({ - ssc: true - })).to.contain('-y'); - }); - - it('should return -ssc- with flag { ssc: false }', function () { - expect(switches({ - ssc: false - })).to.contain('-ssc-'); - }); - - it('should return non default booleans when specified', function () { - var r = switches({ - so: true, - spl: true, - ssw: true, - y: false - }); - expect(r).to.contain('-so'); - expect(r).to.contain('-spl'); - expect(r).to.contain('-ssc'); - expect(r).to.contain('-ssw'); - expect(r).not.to.contain('-y'); - }); - - it('should return complex values when needed', function () { - var r = switches({ - ssc: true, - ssw: true, - mx0: true - }); - expect(r).to.contain('-ssc'); - expect(r).to.contain('-ssw'); - expect(r).to.contain('-mx0'); - expect(r).to.contain('-y'); - }); - - it('should return complex values with spaces and quotes', function () { - var r = switches({ - ssc: true, - ssw: true, - m0: '=BCJ', - m1: '=LZMA:d=21', - p: 'My Super Pasw,àù£*', - sfx: '7zSD.sfx', - }); - expect(r).to.contain('-ssc'); - expect(r).to.contain('-ssw'); - expect(r).to.contain('-m0=BCJ'); - expect(r).to.contain('-m1=LZMA:d=21'); - expect(r).to.contain('-p"My Super Pasw,àù£*"'); - expect(r).to.contain('-y'); - expect(r).to.contain('-sfx7zSD.sfx'); - }); - - it('should works with the `raw` switch', function () { - var r = switches({ - raw: ['-i!*.jpg', '-i!*.png'], - }); - expect(r).to.contain('-i!*.jpg'); - expect(r).to.contain('-i!*.png'); - }); - -}); diff --git a/util/binary.mjs b/util/binary.mjs deleted file mode 100644 index ba5c136..0000000 --- a/util/binary.mjs +++ /dev/null @@ -1,24 +0,0 @@ -'use strict'; - -import { - fileURLToPath -} from 'url'; -import { - dirname, - join, - sep -} from 'path'; - -const __filename = fileURLToPath( - import.meta.url); -const __dirname = dirname(__filename); - -export default function (override = false, binary = '7z') { - let binaryPath = join(__dirname, '..', "binaries", (override == true ? process.platform + sep + 'other32' : process.platform)); - let binaryFilename = (process.platform == "win32") ? binary + '.exe' : binary; - return { - path: binaryPath, - filename: binaryFilename, - filepath: join(binaryPath, binaryFilename) - } -}; diff --git a/util/files.mjs b/util/files.mjs deleted file mode 100644 index 554da9a..0000000 --- a/util/files.mjs +++ /dev/null @@ -1,28 +0,0 @@ -'use strict'; - -import { isUndefined, isArray } from 'node-sys'; - -/** - * Transform a list of files that can be an array or a string into a string - * that can be passed to the `run` function as part of the `command` parameter. - * @param {string|array} files - * @return {string} - */ -export default function (files) { - - if (isUndefined(files)) { - return ''; - } - - let toProcess = ''; - if (isArray(files)) { - files.forEach(function (f) { - toProcess += '"' + f + '" '; - }); - toProcess = toProcess.trim(); - } else { - toProcess = '"' + files + '"'; - } - - return toProcess; -}; diff --git a/util/replaceNativeSeparator.mjs b/util/replaceNativeSeparator.mjs deleted file mode 100644 index c779409..0000000 --- a/util/replaceNativeSeparator.mjs +++ /dev/null @@ -1,14 +0,0 @@ -'use strict'; -import { sep as nativeSeparator } from 'path'; - -/** - * @param {string} path A path with the native directory separator. - * @return {string} A path with / for directory separator. - */ -export default function (path) { - let result = path, next; - while ((next = result.replace(nativeSeparator, '/')) !== result) { - result = next; - } - return result; -}; diff --git a/util/run.mjs b/util/run.mjs deleted file mode 100644 index d20130d..0000000 --- a/util/run.mjs +++ /dev/null @@ -1,144 +0,0 @@ -'use strict'; -import { - EOL -} from 'os'; -import { spawning, isArray } from 'node-sys'; -import when from 'when'; -import { - normalize, - sep -} from 'path'; -import utilSwitches from './switches.mjs'; -import platformBinary from './binary.mjs'; - -/** - * @param {string} binary which binary to use. - * @param {string} command The command to run. - * @param {Array} switches Options for 7-Zip as an array. - * @param {boolean} override should binary directory change? - * - * @progress {string} stdout message. - * @reject {Error} The error issued by 7-Zip. - * @reject {number} Exit code issued by 7-Zip. - * - * @returns {Promise} Promise - */ -export default function (binary = '7z', command = null, switches = {}, override = false) { - return when.promise(function (fulfill, reject, progress) { - - // Parse the command variable. If the command is not a string reject the - // Promise. Otherwise transform the command into two variables: the command - // name and the arguments. - if (typeof command !== 'string' || typeof binary !== 'string') { - return reject(new Error('Command and Binary must be a string')); - } - - // add platform binary to command - let sevenBinary = platformBinary(override, binary); - let cmd = sevenBinary.filepath; - let args = [command.split(' ')[0]]; - - // Parse and add command (non-switches parameters) to `args`. - let regexpCommands = /"((?:\\.|[^"\\])*)"/g; - let commands = command.match(regexpCommands); - if (commands) { - commands.forEach(function (c) { - c = c.replace(/\//g, sep); - c = c.replace(/\\/g, sep); - c = normalize(c); - args.push(c); - }); - } - - // Special treatment for the output switch because it is exposed as a - // parameter in the API and not as a option. Plus wildcards can be passed. - let regexpOutput = /-o"((?:\\.|[^"\\])*)"/g; - let output = command.match(regexpOutput); - if (output) { - args.pop(); - let o = output[0]; - o = o.replace(/\//g, sep); - o = o.replace(/\\/g, sep); - o = o.replace(/"/g, ''); - o = normalize(o); - args.push(o); - } - - if (switches.files) { - let files = switches.files; - delete switches.files; - if (isArray(files)) { - files.forEach(function (s) { - args.push(s); - }); - } else { - args.push(files); - } - - args.push('-r'); - args.push('-aoa'); - } - - // Add switches to the `args` array. - let switchesArray = utilSwitches(switches); - switchesArray.forEach(function (s) { - args.push(s); - }); - - // Remove now double quotes. If present in the spawned process 7-Zip will - // read them as part of the paths (e.g.: create a `"archive.7z"` with - // quotes in the file-name); - args.forEach(function (e, i) { - if (typeof e !== 'string') { - return; - } - if (e.substr(0, 1) !== '-') { - e = e.replace(/^"/, ''); - e = e.replace(/"$/, ''); - args[i] = e; - } - }); - - // Add bb2 to args array so we get file info - args.push('-bb2'); - - // When an stdout is emitted, parse it. If an error is detected in the body - // of the stdout create an new error with the 7-Zip error message as the - // error's message. Otherwise progress with stdout message. - let err; - let reg = new RegExp('Error:(' + EOL + '|)?(.*)', 'i'); - let onprogress = (object) => { - progress(object.output); - return args; - }; - - let onerror = (data) => { - let res = reg.exec(data); - if (res) { - err = new Error(res[2].substr(0, res[2].length - 1)); - return err; - } - }; - - let res = { - cmd: cmd, - args: args, - options: { - stdio: 'pipe', - onprogress: onprogress, - onerror: onerror - } - }; - - spawning(res.cmd, res.args, res.options) - .then((data) => { - if (data === args) - return fulfill(args); - - return reject(err); - }) - .catch((err) => { - return reject(err); - }); - }); -}; diff --git a/util/switches.mjs b/util/switches.mjs deleted file mode 100644 index 93d8f70..0000000 --- a/util/switches.mjs +++ /dev/null @@ -1,64 +0,0 @@ -'use strict'; - -/** - * Transform an object of options into an array that can be passed to the - * spawned child process. - * @param {Object} switches An object of options - * @return {array} Array to pass to the `run` function. - */ -export default function (switches) { - - // Default value for switches - switches = switches || {}; - - var a = []; - // Set default values of boolean switches - switches.so = (switches.so === true) ? true : false; - switches.spl = (switches.spl === true) ? true : false; - switches.ssc = (switches.ssc === false) ? false : true; - switches.ssw = (switches.ssw === true) ? true : false; - switches.y = (switches.y === false) ? false : true; - - var s; - /*jshint forin:false*/ - for (s in switches) { - - // Switches that are set or not. Just add them to the array if they are - // present. Differ the `ssc` switch treatment to later in the function. - if (switches[s] === true && s !== 'ssc') { - a.push('-' + s); - } - - // Switches with a value. Detect if the value contains a space. If it does - // wrap the value with double quotes. Else just add the switch and its value - // to the string. Doubles quotes are used for parsing with a RegExp later. - if (typeof switches[s] !== 'boolean') { - - // Special treatment for wildcards - if (s === 'wildcards') { - a.unshift(switches.wildcards); - } - - // Allow raw switches to be added to the command, repeating switches like - // -i is not possible otherwise. - else if (s === 'raw') { - switches.raw.forEach(function (rawValue) { - a.push(rawValue); - }); - } else if (switches[s].indexOf(' ') === -1) { - a.push('-' + s + switches[s]); - } else { - a.push('-' + s + '"' + switches[s] + '"'); - } - } - - // Special treatment for `-ssc` - if (s === 'ssc') { - a.push((switches.ssc === true) ? '-ssc' : '-ssc-'); - } - - } - - return a; - -};