@xhmikosr/bin-wrapper 14.2.4 → 14.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +102 -69
- package/package.json +2 -3
- package/readme.md +4 -0
package/index.js
CHANGED
|
@@ -2,34 +2,42 @@ import {promises as fs} from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import binCheck from '@xhmikosr/bin-check';
|
|
4
4
|
import binaryVersionCheck from 'binary-version-check';
|
|
5
|
-
import
|
|
5
|
+
import downloader from '@xhmikosr/downloader';
|
|
6
6
|
import osFilterObject from '@xhmikosr/os-filter-obj';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* @
|
|
12
|
-
* @api public
|
|
9
|
+
* @typedef {Object} BinWrapperOptions
|
|
10
|
+
* @property {number} [strip=1] - Number of leading paths to strip from the archive.
|
|
11
|
+
* @property {boolean} [skipCheck=false] - Skip binary checks.
|
|
13
12
|
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @typedef {Object} SourceFile
|
|
16
|
+
* @property {string} url - The URL of the file.
|
|
17
|
+
* @property {string} [os] - The operating system the file is for.
|
|
18
|
+
* @property {string} [arch] - The architecture the file is for.
|
|
19
|
+
*/
|
|
20
|
+
|
|
14
21
|
export default class BinWrapper {
|
|
22
|
+
/**
|
|
23
|
+
* @param {BinWrapperOptions} [options]
|
|
24
|
+
*/
|
|
15
25
|
constructor(options = {}) {
|
|
16
|
-
|
|
26
|
+
const {strip = 1, skipCheck = false} = options;
|
|
17
27
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
22
|
-
this.options.strip = 1;
|
|
23
|
-
}
|
|
28
|
+
this.options = {
|
|
29
|
+
strip: Math.max(0, strip),
|
|
30
|
+
skipCheck,
|
|
31
|
+
};
|
|
24
32
|
}
|
|
25
33
|
|
|
26
34
|
/**
|
|
27
35
|
* Get or set files to download
|
|
28
36
|
*
|
|
29
|
-
* @param {
|
|
30
|
-
* @param {
|
|
31
|
-
* @param {
|
|
32
|
-
* @
|
|
37
|
+
* @param {string} [src] - The source URL of the file.
|
|
38
|
+
* @param {string} [os] - The operating system the file is for.
|
|
39
|
+
* @param {string} [arch] - The architecture the file is for.
|
|
40
|
+
* @returns {SourceFile[]|undefined|this} - Returns the source files if no arguments are provided, otherwise returns `this`.
|
|
33
41
|
*/
|
|
34
42
|
src(src, os, arch) {
|
|
35
43
|
if (arguments.length === 0) {
|
|
@@ -45,8 +53,8 @@ export default class BinWrapper {
|
|
|
45
53
|
/**
|
|
46
54
|
* Get or set the destination
|
|
47
55
|
*
|
|
48
|
-
* @param {
|
|
49
|
-
* @
|
|
56
|
+
* @param {string} [dest] - The destination path.
|
|
57
|
+
* @returns {string|undefined|this} - Returns the destination if no arguments are provided, otherwise returns `this`.
|
|
50
58
|
*/
|
|
51
59
|
dest(dest) {
|
|
52
60
|
if (arguments.length === 0) {
|
|
@@ -54,14 +62,15 @@ export default class BinWrapper {
|
|
|
54
62
|
}
|
|
55
63
|
|
|
56
64
|
this._dest = dest;
|
|
65
|
+
|
|
57
66
|
return this;
|
|
58
67
|
}
|
|
59
68
|
|
|
60
69
|
/**
|
|
61
70
|
* Get or set the binary
|
|
62
71
|
*
|
|
63
|
-
* @param {
|
|
64
|
-
* @
|
|
72
|
+
* @param {string} [bin] - The binary name.
|
|
73
|
+
* @returns {string|undefined|this} - Returns the binary name if no arguments are provided, otherwise returns `this`.
|
|
65
74
|
*/
|
|
66
75
|
use(bin) {
|
|
67
76
|
if (arguments.length === 0) {
|
|
@@ -69,14 +78,15 @@ export default class BinWrapper {
|
|
|
69
78
|
}
|
|
70
79
|
|
|
71
80
|
this._use = bin;
|
|
81
|
+
|
|
72
82
|
return this;
|
|
73
83
|
}
|
|
74
84
|
|
|
75
85
|
/**
|
|
76
86
|
* Get or set a semver range to test the binary against
|
|
77
87
|
*
|
|
78
|
-
* @param {
|
|
79
|
-
* @
|
|
88
|
+
* @param {string} [range] - The semver range.
|
|
89
|
+
* @returns {string|undefined|this} - Returns the semver range if no arguments are provided, otherwise returns `this`.
|
|
80
90
|
*/
|
|
81
91
|
version(range) {
|
|
82
92
|
if (arguments.length === 0) {
|
|
@@ -84,100 +94,123 @@ export default class BinWrapper {
|
|
|
84
94
|
}
|
|
85
95
|
|
|
86
96
|
this._version = range;
|
|
97
|
+
|
|
87
98
|
return this;
|
|
88
99
|
}
|
|
89
100
|
|
|
90
101
|
/**
|
|
91
102
|
* Get path to the binary
|
|
92
103
|
*
|
|
93
|
-
* @
|
|
104
|
+
* @returns {string} - The full path to the binary.
|
|
94
105
|
*/
|
|
95
106
|
path() {
|
|
96
107
|
return path.join(this.dest(), this.use());
|
|
97
108
|
}
|
|
98
109
|
|
|
99
110
|
/**
|
|
100
|
-
*
|
|
111
|
+
* Get the source URLs matching the current OS and arch
|
|
101
112
|
*
|
|
102
|
-
* @
|
|
103
|
-
* @api public
|
|
113
|
+
* @returns {string[]}
|
|
104
114
|
*/
|
|
105
|
-
|
|
106
|
-
return this.
|
|
107
|
-
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
115
|
+
resolvedUrls() {
|
|
116
|
+
return osFilterObject(this.src() || []).map(file => file.url);
|
|
117
|
+
}
|
|
110
118
|
|
|
111
|
-
|
|
112
|
-
|
|
119
|
+
/**
|
|
120
|
+
* Check for the binary and download it if missing, then optionally verify it works.
|
|
121
|
+
*
|
|
122
|
+
* @param {string[]} [cmd=['--version']] - Arguments passed to the binary when checking it.
|
|
123
|
+
* @returns {Promise<void>}
|
|
124
|
+
*/
|
|
125
|
+
async run(cmd = ['--version']) {
|
|
126
|
+
await this.findExisting();
|
|
127
|
+
|
|
128
|
+
if (this.options.skipCheck) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
await this.runCheck(cmd);
|
|
113
133
|
}
|
|
114
134
|
|
|
115
135
|
/**
|
|
116
136
|
* Run binary check
|
|
117
137
|
*
|
|
118
|
-
* @param {
|
|
138
|
+
* @param {string[]} cmd - Arguments to pass to the binary.
|
|
139
|
+
* @returns {Promise<void>}
|
|
119
140
|
* @api private
|
|
120
141
|
*/
|
|
121
|
-
runCheck(cmd) {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
142
|
+
async runCheck(cmd) {
|
|
143
|
+
const works = await binCheck(this.path(), cmd);
|
|
144
|
+
if (!works) {
|
|
145
|
+
throw new Error(`The "${this.path()}" binary doesn't seem to work correctly`);
|
|
146
|
+
}
|
|
126
147
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
});
|
|
148
|
+
if (this.version()) {
|
|
149
|
+
await binaryVersionCheck(this.path(), this.version());
|
|
150
|
+
}
|
|
131
151
|
}
|
|
132
152
|
|
|
133
153
|
/**
|
|
134
|
-
*
|
|
154
|
+
* Check whether the binary exists; download it if not.
|
|
135
155
|
*
|
|
156
|
+
* @returns {Promise<void>}
|
|
136
157
|
* @api private
|
|
137
158
|
*/
|
|
138
|
-
findExisting() {
|
|
139
|
-
|
|
159
|
+
async findExisting() {
|
|
160
|
+
try {
|
|
161
|
+
await fs.access(this.path());
|
|
162
|
+
} catch (error) {
|
|
140
163
|
if (error?.code === 'ENOENT') {
|
|
141
|
-
|
|
164
|
+
await this.download();
|
|
165
|
+
} else {
|
|
166
|
+
throw error;
|
|
142
167
|
}
|
|
143
|
-
|
|
144
|
-
throw error;
|
|
145
|
-
});
|
|
168
|
+
}
|
|
146
169
|
}
|
|
147
170
|
|
|
148
171
|
/**
|
|
149
|
-
* Download files
|
|
172
|
+
* Download files matching the current OS/arch and make them executable.
|
|
150
173
|
*
|
|
174
|
+
* @returns {Promise<void>}
|
|
151
175
|
* @api private
|
|
152
176
|
*/
|
|
153
|
-
download() {
|
|
154
|
-
const
|
|
177
|
+
async download() {
|
|
178
|
+
const urls = this.resolvedUrls();
|
|
155
179
|
|
|
156
|
-
if (
|
|
157
|
-
|
|
180
|
+
if (urls.length === 0) {
|
|
181
|
+
throw new Error('No binary found matching your system. It\'s probably not supported.');
|
|
158
182
|
}
|
|
159
183
|
|
|
160
|
-
const
|
|
161
|
-
|
|
162
|
-
return Promise.all(urls.map(url =>
|
|
163
|
-
download(url, this.dest(), {
|
|
184
|
+
const results = await Promise.all(urls.map(url =>
|
|
185
|
+
downloader(url, this.dest(), {
|
|
164
186
|
extract: true,
|
|
165
187
|
decompress: {
|
|
166
188
|
strip: this.options.strip,
|
|
167
189
|
},
|
|
168
|
-
})))
|
|
169
|
-
const resultFiles = result.flatMap((item, index) => {
|
|
170
|
-
if (Array.isArray(item)) {
|
|
171
|
-
return item.map(file => file.path);
|
|
172
|
-
}
|
|
190
|
+
})));
|
|
173
191
|
|
|
174
|
-
|
|
175
|
-
|
|
192
|
+
const resultFiles = results.flatMap((item, index) => {
|
|
193
|
+
if (Array.isArray(item)) {
|
|
194
|
+
return item.map(file => file.path);
|
|
195
|
+
}
|
|
176
196
|
|
|
177
|
-
|
|
178
|
-
});
|
|
197
|
+
const parsedUrl = new URL(urls[index]);
|
|
179
198
|
|
|
180
|
-
return
|
|
199
|
+
return path.parse(parsedUrl.pathname).base;
|
|
181
200
|
});
|
|
201
|
+
|
|
202
|
+
await Promise.all(resultFiles
|
|
203
|
+
.filter(Boolean)
|
|
204
|
+
.map(async file => {
|
|
205
|
+
try {
|
|
206
|
+
await fs.chmod(path.join(this.dest(), file), 0o755);
|
|
207
|
+
} catch (error) {
|
|
208
|
+
// We guess the saved name from the URL, but the downloader may
|
|
209
|
+
// have used a different one, so skip a missing file.
|
|
210
|
+
if (error?.code !== 'ENOENT') {
|
|
211
|
+
throw error;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}));
|
|
182
215
|
}
|
|
183
216
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xhmikosr/bin-wrapper",
|
|
3
|
-
"version": "14.
|
|
3
|
+
"version": "14.3.0",
|
|
4
4
|
"description": "Binary wrapper that makes your programs seamlessly available as local dependencies",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
],
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"@xhmikosr/bin-check": "^8.2.1",
|
|
45
|
-
"@xhmikosr/downloader": "^16.1.
|
|
45
|
+
"@xhmikosr/downloader": "^16.1.3",
|
|
46
46
|
"@xhmikosr/os-filter-obj": "^4.1.0",
|
|
47
47
|
"binary-version-check": "^6.1.0"
|
|
48
48
|
},
|
|
@@ -56,7 +56,6 @@
|
|
|
56
56
|
},
|
|
57
57
|
"xo": {
|
|
58
58
|
"rules": {
|
|
59
|
-
"promise/prefer-await-to-then": "off",
|
|
60
59
|
"unicorn/prevent-abbreviations": "off"
|
|
61
60
|
}
|
|
62
61
|
}
|
package/readme.md
CHANGED