techno-express/node-7z

View on GitHub
lib/createSfx.js

Summary

Maintainability
A
1 hr
Test Coverage
'use strict';

const add = require('./add'),
  when = require('when'),
  path = require('path'),
  fs = require('fs-extra'),
  binary = require('../util/path'),
  platformTitle = {
    win32: 'Windows OS',
    darwin: 'Apple macOS',
    linux: 'Linux OS'
  },
  title = ' installation package created on ' + platformTitle[process.platform] + '.',
  prompt = 'Do you want to install ';

// our parent folder path
const pwd = __dirname.split(path.sep);

function getPath(module, folder = pwd) {
  if (folder.length < 1) {
    return null;
  }

  const nodePath = folder.concat(["node_modules"]).join(path.sep);
  const parent = module ? path.join(nodePath, module) : nodePath;
  if (fs.existsSync(parent)) {
    return parent.includes('node_modules') ? path.join(parent, '..' + path.sep + '..') : nodePath;
  }

  let find = getPath(module, folder.slice(0, -1));
  if (!find) {
    console.error('Could not find NPM node_modules for: ' + module);
    return path.join(__dirname, '..');
  }

  return find;
};

/**
 * Creates self extracting archive, an Installation Package.
 *
 * @param {String} name Application name.
 * @param {Array} files Files to add.
 * @param {String} destination Application root for the `SfxPackages` directory, will default to package root.
 * - All Sfx package archives are stored in the **created** `SfxPackages` directory.
 * - The `destination` directory must already exists.
 * @param {Object} options Object for Installer config and 7-zip switch options.
 *
 * `{`
 *
 * `title:` - Window title message, Default "`name` installation package created on `Current running platform OS`"
 *
 * `beginPrompt:` - Begin Prompt message, Default "Do you want to install `name`?""
 *
 * `installPath:` - "path_to_extract", Sets the extraction path. The extraction folder will not be deleted after the extraction.
 *
 * `progress:` - Value can be "yes" or "no". Default value is "yes".
 *
 * `runProgram:` - Command for executing. Default value is "setup.exe".
 * Substring `% % T` will be replaced with path to temporary folder,
 * where files were extracted
 *
 * `directory:` - Directory prefix for `RunProgram`. Default value is `.\`
 *
 * `executeFile:` Name of file for executing
 *
 * `executeParameters:` Parameters for `ExecuteFile`
 *
 * `}`
 *
 * `NOTE:` There are two ways to run program: `RunProgram` and `ExecuteFile`.
 * - Use `RunProgram`, if you want to run some program from .7z archive.
 * - Use `ExecuteFile`, if you want to open some document from .7z archive or
 * if you want to execute some command from Windows.
 * @param {String} type Application type `gui` or `console`. Default `gui`. Only `console` possible on **Linux** and **Mac** OS.
 * @param {String} platform What platform application targeting? Either `win32`, `darwin`, or `linux`.
 * @param {String} extension Binary extension name.
 *
 * @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
 */
module.exports = function (
  name,
  files,
  destination = '',
  options = {
    title: null,
    beginPrompt: null,
    progress: null
  },
  type = 'gui',
  platform = 'win32',
  extension = '.exe') {
  return when.promise(function (resolve, reject, progress) {
    let directory = (destination != '' && fs.existsSync(destination)) ? destination : getPath('when');
    let SfxDirectory = path.join(directory, 'SfxPackages');
    fs.ensureDirSync(SfxDirectory);
    let override = ((process.platform == 'win32') && (platform == 'linux' || platform == 'darwin'));
    let binaryDirectory = binary({}, override);
    let configFile = path.join(binaryDirectory.path, 'config.txt');
    //let configFile = path.join(SfxDirectory, 'config.txt');
    let config = fs.createWriteStream(configFile, {
      flags: 'w+',
      encoding: 'utf8'
    });

    let text = '';
    config.write(';!@Install@!UTF-8!' + "\n");
    text = options.title || name + title;
    config.write('Title=' + text + "\n");
    text = options.beginPrompt || prompt + name;
    config.write('BeginPrompt=' + text + "?\n");
    text = options.progress || 'no';
    config.write('Progress=' + text + "\n");

    if (options.runProgram)
      config.write('RunProgram=' + options.runProgram + "\n");
    if (options.directory)
      config.write('Directory=' + options.directory + "\n");
    if (options.installPath)
      config.write('InstallPath=' + options.installPath + "\n");
    if (options.executeFile)
      config.write('ExecuteFile=' + options.executeFile + "\n");
    if (options.executeParameters)
      config.write('ExecuteParameters=' + options.executeParameters + "\n");

    config.write(';!@InstallEnd@!' + "\n");
    config.close();

    delete options.title;
    delete options.beginPrompt;
    delete options.installPath;
    delete options.progress;
    delete options.runProgram;
    delete options.directory;
    delete options.executeFile;
    delete options.executeParameters;

    if (type == 'gui')
      var sfxModule = '7zSD' + (process.platform == 'win32' ? 'win32' : 'other32');
    else
      var sfxModule = (platform == 'win32' ? '7zS2con' : '7zCon') + (platform == 'win32' ? 'other32' : platform);

    let sfx = name.includes(extension) ? name : name + extension;
    let list = Array.isArray(files) ? [configFile].concat(files) : configFile + ' ' + files;
    sfx = path.join(SfxDirectory, sfx);
    let params = Object.assign(options, {
      sfx: sfxModule + '.sfx'
    });

    return add(sfx, list, params, override)
      .progress((data) => {
        return progress(data);
      })
      .then((data) => {
        fs.unlink(configFile, (err) => {
          if (err) console.error(err);
          if (fs.existsSync(sfx)) {
            return resolve(sfx);
          } else {
            console.error(data);
            return reject('Failed! The Sfx application could not be created!');
          }
        });
      })
      .catch((err) => {
        fs.removeSync(configFile);
        return reject(err)
      });
  });
}