node-7z-threetwo
Version:
A CommonJs and ESM frontend to 7-Zip, downloads binaries in for Linux, Windows, and Mac OSX, with methods to create SFX self extracting 7z archives targeting different platforms.
464 lines (438 loc) • 18.9 kB
JavaScript
;
import fs from 'fs-extra';
import { fileURLToPath } from 'url';
import { dirname, join, sep } from 'path';
import spawn from 'cross-spawn';
import unCompress from 'all-unpacker';
import retryPromise from 'retrying-promise';
import fetching from 'node-wget-fetch';
import system_installer from 'system-installer';
import macos_release from 'macos-release';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const versionCompare = (left, right) => {
if (typeof left + typeof right != 'stringstring') return false;
let a = left.split('.');
let b = right.split('.');
let i = 0;
let len = Math.max(a.length, b.length);
for (; i < len; i++) {
if (
(a[i] && !b[i] && parseInt(a[i]) > 0) ||
parseInt(a[i]) > parseInt(b[i])
) {
return 1;
} else if (
(b[i] && !a[i] && parseInt(b[i]) > 0) ||
parseInt(a[i]) < parseInt(b[i])
) {
return -1;
}
}
return 0;
};
const appleOs =
process.platform == 'darwin' ? macos_release.version : '99.99.99',
macOsVersion = versionCompare(appleOs, '10.11.12') == 1 ? '10.15' : '10.11',
_7zAppUrl = 'https://7-zip.org/a/',
cwd = process.cwd(),
binaryDestination = join(__dirname, 'binaries', process.platform);
const windowsPlatform = {
source: join(cwd, '7z1900-extra.7z'),
destination: join(cwd, 'win32'),
url: 'https://d.7-zip.org/a/',
filename: '7z1900-extra.7z',
extraName: 'lzma1900.7z',
extractFolder: '',
appLocation: '',
binaryFiles: ['Far', 'x64', '7za.dll', '7za.exe', '7zxa.dll'],
binaryDestinationDir: join(__dirname, 'binaries', 'win32'),
sfxModules: ['7zr.exe', '7zS2.sfx', '7zS2con.sfx', '7zSD.sfx'],
platform: 'win32',
binary: '7za.exe',
extraSourceFile: join(cwd, 'win32', 'lzma1900.7z'),
};
const windowsOtherPlatform = {
source: join(cwd, '7z1604-extra.7z'),
destination: join(cwd, 'other32'),
url: 'https://d.7-zip.org/a/',
filename: '7z1604-extra.7z',
extraName: 'lzma1604.7z',
extractFolder: '',
appLocation: '',
binaryFiles: ['Far', 'x64', '7za.dll', '7za.exe', '7zxa.dll'],
binaryDestinationDir: join(__dirname, 'binaries', 'win32', 'other32'),
sfxModules: ['7zr.exe', '7zS2.sfx', '7zS2con.sfx', '7zSD.sfx'],
platform: 'win32',
binary: '7za.exe',
extraSourceFile: join(cwd, 'other32', 'lzma1604.7z'),
};
// const linuxPlatform = {
// source: join(cwd, 'p7zip_16.02_x86_linux_bin.tar.bz2'),
// destination: join(cwd, 'linux'),
// url: 'https://iweb.dl.sourceforge.net/project/p7zip/p7zip/16.02/',
// filename: 'p7zip_16.02_x86_linux_bin.tar.bz2',
// extraName: 'lzma1604.7z',
// extractFolder: 'p7zip_16.02',
// appLocation: 'bin',
// binaryFiles: ['7z', '7z.so', '7za', '7zCon.sfx', '7zr', 'Codecs'],
// binaryDestinationDir: join(__dirname, 'binaries', 'linux'),
// sfxModules: ['7zS2.sfx', '7zS2con.sfx', '7zSD.sfx'],
// platform: 'linux',
// binary: '7za',
// extraSourceFile: join(cwd, 'linux', 'lzma1604.7z'),
// };
const macVersion =
macOsVersion == '10.15'
? 'p7zip-16.02-macos10.15.pkg'
: 'p7zip-16.02-macos10.11.pkg';
const appleMacPlatform = {
source: join(cwd, macVersion),
destination: join(cwd, 'darwin'),
url: 'https://raw.githubusercontent.com/rudix-mac/packages/master/',
filename: macVersion,
extraName: 'lzma1604.7z',
extractFolder: '',
appLocation: 'usr/local/lib/p7zip',
binaryFiles: ['7z', '7z.so', '7za', '7zCon.sfx', '7zr', 'Codecs'],
binaryDestinationDir: join(__dirname, 'binaries', 'darwin'),
sfxModules: ['7zS2.sfx', '7zS2con.sfx', '7zSD.sfx'],
platform: 'darwin',
binary: '7za',
extraSourceFile: join(cwd, 'darwin', 'lzma1604.7z'),
};
function retrieve(
path = {
url: '',
dest: '',
}
) {
console.log('Downloading ' + path.url);
return new Promise((resolve, reject) => {
fetching
.wget(path.url, path.dest)
.then((info) => resolve(info))
.catch((err) => reject('Error downloading file: ' + err));
});
}
function platformUnpacker(platformData = windowsPlatform) {
return new retryPromise(
{
retries: 5,
},
(resolve, retry) => {
retrieve({
url: platformData.url + platformData.filename,
dest: platformData.source,
})
.then(() => {
console.log('Extracting: ' + platformData.filename);
if (platformData.platform == 'darwin') {
let destination = platformData.destination;
if (process.platform == 'win32') {
macUnpack(platformData)
.then(() => {
return resolve('darwin');
})
.catch((err) => retry(err));
} else {
unpack(platformData.source, destination)
.then((data) => {
console.log(
'Decompressing: p7zipinstall.pkg/Payload'
);
unpack(
join(
destination,
'p7zipinstall.pkg',
'Payload'
),
destination
)
.then(() => {
console.log(
'Decompressing: Payload'
);
unpack(
join(destination, 'Payload'),
destination,
platformData.appLocation +
sep +
'*'
)
.then(() => {
return resolve('darwin');
})
.catch((err) => retry(err));
})
.catch((err) => retry(err));
})
.catch((err) => retry(err));
}
} else if (platformData.platform == 'win32') {
unpack(platformData.source, platformData.destination)
.then(() => {
return resolve('win32');
})
.catch((err) => retry(err));
} else if (platformData.platform == 'linux') {
// unpack(platformData.source, platformData.destination)
// .then(() => {
// const system = system_installer.packager();
// const toInstall = (system.packager == 'yum' || system.packager == 'dnf') ?
// 'glibc.i686' : 'libc6-i386';
// if (process.platform == 'linux')
// system_installer.installer(toInstall).then(() => {
// return resolve('linux');
// });
// else
// return resolve('linux');
// })
// .catch((err) => retry(err));
} else if (fetching.isString(platformData.platform)) {
unpack(platformData.source, platformData.destination)
.then(() => {
return resolve(platformData.platform);
})
.catch((err) => retry(err));
}
})
.catch((err) => retry(err));
}
).catch((err) => console.error(err));
}
function unpack(source, destination, toCopy) {
return new Promise((resolve, reject) => {
return unCompress.unpack(
source,
{
files: toCopy == null ? '' : toCopy,
targetDir: destination,
forceOverwrite: true,
noDirectory: true,
quiet: true,
},
(err, files, text) => {
if (err) return reject(err);
console.log(text);
return resolve(files);
}
);
});
}
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);
return spawnSync(cmd, extraArgs);
}
function macUnpack(
dataFor = appleMacPlatform,
dataForOther = windowsOtherPlatform
) {
return new Promise((resolve, reject) => {
retrieve({
url: dataForOther.url + '7z1805-extra.7z',
dest: '.' + sep + '7z-extra.7z',
})
.then(() => {
let destination = join(cwd, 'other');
unpack(join(__dirname, '7z-extra.7z'), destination)
.then(() => {
extraUnpack(
join(__dirname, 'other', '7za.exe'),
dataFor.source,
dataFor.destination
);
console.log(
'Decompressing: ' + 'p7zip-16.02-macos10.15'
);
unpack(
join(dataFor.destination, 'p7zip-16.02-macos10.15'),
dataFor.destination
)
.then(() => {
fs.emptyDir(destination).then(() => {
fs.unlink(
join(__dirname, '7z-extra.7z')
).then(() => {
fs.removeSync(destination);
return resolve('darwin');
});
});
})
.catch(() => {
fs.emptyDir(destination).then(() => {
fs.unlink(
join(__dirname, '7z-extra.7z')
).then(() => {
fs.removeSync(destination);
return resolve('darwin');
});
});
});
})
.catch((err) => reject);
})
.catch((err) => reject);
});
}
function spawnSync(spCmd = '', spArgs = []) {
let doUnpack = spawn.sync(spCmd, spArgs, {
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), 755);
} catch (err) {
console.error(err);
}
});
}
[windowsOtherPlatform, windowsPlatform, appleMacPlatform].forEach((dataFor) => {
fs.mkdir(dataFor.destination, (err) => {
if (err) {
}
retrieve({
url: _7zAppUrl + dataFor.extraName,
dest: dataFor.extraSourceFile,
})
.then(() => {
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 == '7zCon.sfx') {
file = '7zCon' + dataFor.platform + '.sfx';
let location = join(
binaryDestination,
process.platform == 'win32'
? 'other32'
: ''
);
to = join(location, file);
fs.moveSync(from, to, {
overwrite: true,
});
makeExecutable([file], location);
console.log(
'Sfx module ' +
file +
' copied successfully!'
);
} else if (
dataFor.platform == process.platform
) {
fs.moveSync(from, to, {
overwrite: true,
});
if (dataFor.platform != 'win32')
makeExecutable(
[file],
dataFor.binaryDestinationDir
);
}
} catch (err) {
console.error(err);
}
});
console.log('Binaries copied successfully!');
fs.unlinkSync(dataFor.source);
if (process.platform == dataFor.platform) {
setTimeout(() => {
extraUnpack(
join(
dataFor.binaryDestinationDir,
dataFor.binary
),
dataFor.extraSourceFile,
binaryDestination,
dataFor.sfxModules
);
fs.unlink(dataFor.extraSourceFile, (err) => {
if (err) throw err;
dataFor.sfxModules.forEach((file) => {
let name = file.replace(
/.sfx/g,
(dataFor.destination.includes(
'win32'
)
? ''
: 'other32') + '.sfx'
);
let to = join(
binaryDestination,
name.includes('other32') &&
process.platform == 'win32'
? 'other32'
: '',
name
);
if (name.includes('other32')) {
fs.renameSync(
join(binaryDestination, file),
to
);
}
console.log(
'Sfx module ' +
name +
' copied successfully!'
);
});
fs.removeSync(dataFor.destination);
});
}, 5000);
} else {
fs.removeSync(dataFor.destination);
}
})
.catch((err) => {
console.error('Unpacking for platform failed.');
throw err;
});
})
.catch((err) => {
console.error('Error downloading file: ' + err);
});
});
// Linux 7za binaries
const sourceDirectory = './7zbin';
const destinationDirectory = './binaries/linux';
if (!fs.existsSync(destinationDirectory)) {
fs.mkdirSync(destinationDirectory, { recursive: true });
}
//copy directory content including subfolders
fs.copy(sourceDirectory, destinationDirectory, (err) => {
if (err) {
console.log(
'Failed to copy 7za binaries to the destination folder.'
);
console.error(err);
} else {
console.log(
'Successfully moved 7za binaries to the destination folder.'
);
}
});
});