installer.mjs
#!/usr/bin/env node
'use strict';
import fs from 'fs-extra';
import {
fileURLToPath
} from 'url';
import {
dirname,
join,
sep,
} from 'path';
import { spawnSync } from 'child_process';
import { unpack } from 'node-unar';
import { wget, isString } from 'node-wget-fetch';
import { isWindows } from 'node-sys';
const __filename = fileURLToPath(
import.meta.url);
const __dirname = dirname(__filename);
const _7zAppUrl = 'https://7-zip.org/a/',
cwd = process.cwd(),
binaryDestination = join(__dirname, 'binaries', process.platform);
const windowsPlatform = {
source: join(cwd, '7z1900.exe'),
destination: join(cwd, 'win32'),
url: 'https://d.7-zip.org/a/',
filename: '7z1900.exe',
extraName: '7z1900-extra.7z',
extractFolder: '',
appLocation: '',
binaryFiles: ['7z.exe', '7z.dll', '7z.sfx', '7zCon.sfx'],
binaryDestinationDir: join(__dirname, 'binaries', 'win32'),
sfxModules: ['7za.dll', '7za.exe', '7zxa.dll'],
platform: 'win32',
binary: '7z.exe',
extraSourceFile: join(cwd, 'win32', '7z1900-extra.7z'),
};
const windowsOtherPlatform = {
source: join(cwd, '7z1604.exe'),
destination: join(cwd, 'other32'),
url: 'https://www.7-zip.org/a/',
filename: '7z1604.exe',
extraName: '7z1604-extra.7z',
extractFolder: '',
appLocation: '',
binaryFiles: ['7z.exe', '7z.dll', '7z.sfx', '7zCon.sfx'],
binaryDestinationDir: join(__dirname, 'binaries', 'win32', 'other32'),
sfxModules: ['7za.dll', '7za.exe', '7zxa.dll'],
platform: 'win32',
binary: '7z.exe',
extraSourceFile: join(cwd, 'other32', '7z1604-extra.7z'),
};
const linuxPlatform = {
source: join(cwd, 'linux-p7zip.7z'),
destination: join(cwd, 'linux'),
url: 'https://github.com/techno-express/p7zip/releases/download/17.02/',
filename: 'linux-p7zip.7z',
extraName: 'lzma1604.7z',
extractFolder: '',
appLocation: '',
binaryFiles: ['7z', '7z.so', '7za', '7zCon.sfx', '7zr', 'Codecs'],
binaryDestinationDir: join(__dirname, 'binaries', 'linux'),
sfxModules: null,
platform: 'linux',
binary: '7z',
extraSourceFile: join(cwd, 'linux', 'lzma1604.7z'),
};
const appleMacPlatform = {
source: join(cwd, 'macos-p7zip.7z'),
destination: join(cwd, 'darwin'),
url: 'https://github.com/techno-express/p7zip/releases/download/17.02/',
filename: 'macos-p7zip.7z',
extraName: 'lzma1604.7z',
extractFolder: '',
appLocation: '',
binaryFiles: ['7z', '7z.so', '7za', '7zCon.sfx', '7zr', 'Codecs'],
binaryDestinationDir: join(__dirname, 'binaries', 'darwin'),
sfxModules: null,
platform: 'darwin',
binary: '7z',
extraSourceFile: join(cwd, 'darwin', 'lzma1604.7z'),
};
function retrieve(path = {
url: '',
dest: ''
}) {
console.log('Downloading ' + path.url);
return wget(path.url, path.dest, { retry: { retries: 5 } })
.then((info) => {
return info;
})
.catch((err) => {
throw ('Error downloading file: ' + err);
});
}
function platformUnpacker(platformData = windowsPlatform) {
return new Promise((resolve, reject) => {
retrieve({
url: platformData.url + platformData.filename,
dest: platformData.source
}).then(() => {
console.log('Extracting: ' + platformData.filename);
if (isString(platformData.platform)) {
unpack(platformData.source, platformData.destination)
.then((results) => {
console.log('Archive of type: ' + results.type)
console.log('Successfully extracted to: ' + results.directory)
return resolve(platformData.platform);
})
.catch((err) => reject(err));
}
}).catch((err) => reject(err));
}).catch((err) => console.error(err));
}
function extraUnpack(cmd = '', source = '', destination = '', toCopy = []) {
let args = ['e', source, '-o' + destination];
let extraArgs = args.concat(toCopy).concat(['-r', '-aos']);
console.log('Running: ' + cmd + ' ' + extraArgs);
let doUnpack = spawnSync(cmd, extraArgs, {
stdio: 'pipe'
});
if (doUnpack.error) {
console.error('Error 7za exited with code ' + doUnpack.error);
console.error('resolve the problem and re-install using:');
console.error('npm install');
}
return doUnpack;
}
function makeExecutable(binary = [], binaryFolder = '') {
binary.forEach((file) => {
try {
if (file == 'Codecs')
file = 'Codecs' + sep + 'Rar.so'
fs.chmodSync(join(binaryFolder, file), 0o777);
} catch (err) {
console.error(err);
}
});
}
let extractionPromises = [];
let platforms = [linuxPlatform, appleMacPlatform, windowsOtherPlatform];
if (isWindows())
platforms = [linuxPlatform, appleMacPlatform, windowsPlatform, windowsOtherPlatform];
platforms.forEach((dataFor) => {
fs.mkdir(dataFor.destination, (err) => {
if (err) { }
});
const extracted = retrieve({
url: _7zAppUrl + dataFor.extraName,
dest: dataFor.extraSourceFile
})
.then(() => {
return platformUnpacker(dataFor)
.then(() => {
dataFor.binaryFiles.forEach((file) => {
try {
let from = join(dataFor.destination, dataFor.extractFolder, dataFor.appLocation, file);
let to = join(dataFor.binaryDestinationDir, file);
if (file.includes('.sfx')) {
file = file.replace(/.sfx/g, dataFor.platform + '.sfx');
let location = join(binaryDestination, (process.platform == 'win32' && !dataFor.source.includes('7z1900.exe') ? 'other32' : ''));
to = join(location, file);
fs.copySync(from, to, {
overwrite: true
});
if (dataFor.platform != 'win32')
makeExecutable([file], location);
console.log('Sfx module ' + file + ' copied successfully!');
} else if (dataFor.platform == process.platform) {
to = to.replace(/7z_/g, '7z');
fs.copySync(from, to, {
overwrite: true
});
if (dataFor.platform != 'win32')
makeExecutable([file.replace(/7z_/g, '7z')], dataFor.binaryDestinationDir);
}
} catch (err) {
throw (err);
}
});
console.log('Binaries copied successfully!');
fs.unlinkSync(dataFor.source);
return dataFor;
})
.catch((err) => {
throw ('Unpacking for platform failed: ' + err);
});
})
.catch((err) => {
throw ('Error downloading file: ' + err);
});
extractionPromises.push(extracted);
});
Promise.all(extractionPromises)
.then((extracted) => {
extracted.forEach(function (dataFor) {
if (dataFor.sfxModules && dataFor.platform == process.platform) {
try {
const directory = isWindows() ? dataFor.binaryDestinationDir : binaryDestination;
extraUnpack(join(binaryDestination, (isWindows() ? '7z.exe' : '7z')),
dataFor.extraSourceFile,
directory,
dataFor.sfxModules
);
dataFor.sfxModules.forEach((file) => {
console.log('Sfx module ' + file + ' copied successfully!');
});
} catch (err) {
console.error(err);
}
}
fs.unlinkSync(dataFor.extraSourceFile);
fs.removeSync(dataFor.destination);
});
})
.catch((err) => console.log(err));