| 'use strict'; |
| const fs = require('fs'); |
| const path = require('path'); |
| const url = require('url'); |
| const pify = require('pify'); |
| const importLazy = require('import-lazy')(require); |
| |
| const binCheck = importLazy('bin-check'); |
| const binVersionCheck = importLazy('bin-version-check'); |
| const download = importLazy('download'); |
| const osFilterObj = importLazy('os-filter-obj'); |
| |
| const statAsync = pify(fs.stat); |
| const chmodAsync = pify(fs.chmod); |
| |
| /** |
| * Initialize a new `BinWrapper` |
| * |
| * @param {Object} options |
| * @api public |
| */ |
| module.exports = class BinWrapper { |
| constructor(options = {}) { |
| this.options = options; |
| |
| if (this.options.strip <= 0) { |
| this.options.strip = 0; |
| } else if (!this.options.strip) { |
| this.options.strip = 1; |
| } |
| } |
| |
| /** |
| * Get or set files to download |
| * |
| * @param {String} src |
| * @param {String} os |
| * @param {String} arch |
| * @api public |
| */ |
| src(src, os, arch) { |
| if (arguments.length === 0) { |
| return this._src; |
| } |
| |
| this._src = this._src || []; |
| this._src.push({ |
| url: src, |
| os, |
| arch |
| }); |
| |
| return this; |
| } |
| |
| /** |
| * Get or set the destination |
| * |
| * @param {String} dest |
| * @api public |
| */ |
| dest(dest) { |
| if (arguments.length === 0) { |
| return this._dest; |
| } |
| |
| this._dest = dest; |
| return this; |
| } |
| |
| /** |
| * Get or set the binary |
| * |
| * @param {String} bin |
| * @api public |
| */ |
| use(bin) { |
| if (arguments.length === 0) { |
| return this._use; |
| } |
| |
| this._use = bin; |
| return this; |
| } |
| |
| /** |
| * Get or set a semver range to test the binary against |
| * |
| * @param {String} range |
| * @api public |
| */ |
| version(range) { |
| if (arguments.length === 0) { |
| return this._version; |
| } |
| |
| this._version = range; |
| return this; |
| } |
| |
| /** |
| * Get path to the binary |
| * |
| * @api public |
| */ |
| path() { |
| return path.join(this.dest(), this.use()); |
| } |
| |
| /** |
| * Run |
| * |
| * @param {Array} cmd |
| * @api public |
| */ |
| run(cmd = ['--version']) { |
| return this.findExisting().then(() => { |
| if (this.options.skipCheck) { |
| return; |
| } |
| |
| return this.runCheck(cmd); |
| }); |
| } |
| |
| /** |
| * Run binary check |
| * |
| * @param {Array} cmd |
| * @api private |
| */ |
| runCheck(cmd) { |
| return binCheck(this.path(), cmd).then(works => { |
| if (!works) { |
| throw new Error(`The \`${this.path()}\` binary doesn't seem to work correctly`); |
| } |
| |
| if (this.version()) { |
| return binVersionCheck(this.path(), this.version()); |
| } |
| |
| return Promise.resolve(); |
| }); |
| } |
| |
| /** |
| * Find existing files |
| * |
| * @api private |
| */ |
| findExisting() { |
| return statAsync(this.path()).catch(error => { |
| if (error && error.code === 'ENOENT') { |
| return this.download(); |
| } |
| |
| return Promise.reject(error); |
| }); |
| } |
| |
| /** |
| * Download files |
| * |
| * @api private |
| */ |
| download() { |
| const files = osFilterObj(this.src() || []); |
| const urls = []; |
| |
| if (files.length === 0) { |
| return Promise.reject(new Error('No binary found matching your system. It\'s probably not supported.')); |
| } |
| |
| files.forEach(file => urls.push(file.url)); |
| |
| return Promise.all(urls.map(url => download(url, this.dest(), { |
| extract: true, |
| strip: this.options.strip |
| }))).then(result => { |
| const resultingFiles = flatten(result.map((item, index) => { |
| if (Array.isArray(item)) { |
| return item.map(file => file.path); |
| } |
| |
| const parsedUrl = url.parse(files[index].url); |
| const parsedPath = path.parse(parsedUrl.pathname); |
| |
| return parsedPath.base; |
| })); |
| |
| return Promise.all(resultingFiles.map(fileName => { |
| return chmodAsync(path.join(this.dest(), fileName), 0o755); |
| })); |
| }); |
| } |
| }; |
| |
| function flatten(arr) { |
| return arr.reduce((acc, elem) => { |
| if (Array.isArray(elem)) { |
| acc.push(...elem); |
| } else { |
| acc.push(elem); |
| } |
| |
| return acc; |
| }, []); |
| } |