@xhmikosr/bin-wrapper 14.2.3 → 14.2.5

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.
Files changed (3) hide show
  1. package/index.js +91 -65
  2. package/package.json +4 -5
  3. package/readme.md +1 -1
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 download from '@xhmikosr/downloader';
5
+ import downloader from '@xhmikosr/downloader';
6
6
  import osFilterObject from '@xhmikosr/os-filter-obj';
7
7
 
8
8
  /**
9
- * Initialize a new `BinWrapper`
10
- *
11
- * @param {Object} options
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
- this.options = options;
26
+ const {strip = 1, skipCheck = false} = options;
17
27
 
18
- if (this.options.strip <= 0) {
19
- this.options.strip = 0;
20
- // eslint-disable-next-line logical-assignment-operators
21
- } else if (!this.options.strip) {
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 {String} src
30
- * @param {String} os
31
- * @param {String} arch
32
- * @api public
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 {String} dest
49
- * @api public
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 {String} bin
64
- * @api public
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 {String} range
79
- * @api public
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,116 @@ 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
- * @api public
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
- * Run
111
+ * Check for the binary and download it if missing, then optionally verify it works.
101
112
  *
102
- * @param {Array} cmd
103
- * @api public
113
+ * @param {string[]} [cmd=['--version']] - Arguments passed to the binary when checking it.
114
+ * @returns {Promise<void>}
104
115
  */
105
- run(cmd = ['--version']) {
106
- return this.findExisting().then(() => {
107
- if (this.options.skipCheck) {
108
- return;
109
- }
116
+ async run(cmd = ['--version']) {
117
+ await this.findExisting();
110
118
 
111
- return this.runCheck(cmd);
112
- });
119
+ if (this.options.skipCheck) {
120
+ return;
121
+ }
122
+
123
+ await this.runCheck(cmd);
113
124
  }
114
125
 
115
126
  /**
116
127
  * Run binary check
117
128
  *
118
- * @param {Array} cmd
129
+ * @param {string[]} cmd - Arguments to pass to the binary.
130
+ * @returns {Promise<void>}
119
131
  * @api private
120
132
  */
121
- runCheck(cmd) {
122
- return binCheck(this.path(), cmd).then(works => {
123
- if (!works) {
124
- throw new Error(`The "${this.path()}" binary doesn't seem to work correctly`);
125
- }
133
+ async runCheck(cmd) {
134
+ const works = await binCheck(this.path(), cmd);
135
+ if (!works) {
136
+ throw new Error(`The "${this.path()}" binary doesn't seem to work correctly`);
137
+ }
126
138
 
127
- if (this.version()) {
128
- return binaryVersionCheck(this.path(), this.version());
129
- }
130
- });
139
+ if (this.version()) {
140
+ await binaryVersionCheck(this.path(), this.version());
141
+ }
131
142
  }
132
143
 
133
144
  /**
134
- * Find existing files
145
+ * Check whether the binary exists; download it if not.
135
146
  *
147
+ * @returns {Promise<void>}
136
148
  * @api private
137
149
  */
138
- findExisting() {
139
- return fs.stat(this.path()).catch(error => {
150
+ async findExisting() {
151
+ try {
152
+ await fs.access(this.path());
153
+ } catch (error) {
140
154
  if (error?.code === 'ENOENT') {
141
- return this.download();
155
+ await this.download();
156
+ } else {
157
+ throw error;
142
158
  }
143
-
144
- throw error;
145
- });
159
+ }
146
160
  }
147
161
 
148
162
  /**
149
- * Download files
163
+ * Download files matching the current OS/arch and make them executable.
150
164
  *
165
+ * @returns {Promise<void>}
151
166
  * @api private
152
167
  */
153
- download() {
168
+ async download() {
154
169
  const files = osFilterObject(this.src() || []);
155
170
 
156
171
  if (files.length === 0) {
157
- return Promise.reject(new Error('No binary found matching your system. It\'s probably not supported.'));
172
+ throw new Error('No binary found matching your system. It\'s probably not supported.');
158
173
  }
159
174
 
160
175
  const urls = files.map(file => file.url);
161
176
 
162
- return Promise.all(urls.map(url =>
163
- download(url, this.dest(), {
177
+ const results = await Promise.all(urls.map(url =>
178
+ downloader(url, this.dest(), {
164
179
  extract: true,
165
180
  decompress: {
166
181
  strip: this.options.strip,
167
182
  },
168
- }))).then(result => {
169
- const resultFiles = result.flatMap((item, index) => {
170
- if (Array.isArray(item)) {
171
- return item.map(file => file.path);
172
- }
183
+ })));
173
184
 
174
- const parsedUrl = new URL(files[index].url);
175
- const parsedPath = path.parse(parsedUrl.pathname);
185
+ const resultFiles = results.flatMap((item, index) => {
186
+ if (Array.isArray(item)) {
187
+ return item.map(file => file.path);
188
+ }
176
189
 
177
- return parsedPath.base;
178
- });
190
+ const parsedUrl = new URL(files[index].url);
179
191
 
180
- return Promise.all(resultFiles.map(file => fs.chmod(path.join(this.dest(), file), 0o755)));
192
+ return path.parse(parsedUrl.pathname).base;
181
193
  });
194
+
195
+ await Promise.all(resultFiles
196
+ .filter(Boolean)
197
+ .map(async file => {
198
+ try {
199
+ await fs.chmod(path.join(this.dest(), file), 0o755);
200
+ } catch (error) {
201
+ // We guess the saved name from the URL, but the downloader may
202
+ // have used a different one, so skip a missing file.
203
+ if (error?.code !== 'ENOENT') {
204
+ throw error;
205
+ }
206
+ }
207
+ }));
182
208
  }
183
209
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xhmikosr/bin-wrapper",
3
- "version": "14.2.3",
3
+ "version": "14.2.5",
4
4
  "description": "Binary wrapper that makes your programs seamlessly available as local dependencies",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -42,21 +42,20 @@
42
42
  ],
43
43
  "dependencies": {
44
44
  "@xhmikosr/bin-check": "^8.2.1",
45
- "@xhmikosr/downloader": "^16.1.2",
46
- "@xhmikosr/os-filter-obj": "^4.0.0",
45
+ "@xhmikosr/downloader": "^16.1.3",
46
+ "@xhmikosr/os-filter-obj": "^4.1.0",
47
47
  "binary-version-check": "^6.1.0"
48
48
  },
49
49
  "devDependencies": {
50
50
  "ava": "^7.0.0",
51
51
  "c8": "^11.0.0",
52
52
  "isexe": "^4.0.0",
53
- "nock": "^14.0.12",
53
+ "nock": "^14.0.15",
54
54
  "tempy": "^3.2.0",
55
55
  "xo": "^2.0.2"
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
@@ -1,4 +1,4 @@
1
- # bin-wrapper [![npm version](https://img.shields.io/npm/v/@xhmikosr/bin-wrapper?logo=npm&logoColor=fff)](https://www.npmjs.com/package/@xhmikosr/bin-wrapper) [![CI Status](https://img.shields.io/github/actions/workflow/status/XhmikosR/bin-wrapper/ci.yml?branch=master&label=CI&logo=github)](https://github.com/XhmikosR/bin-wrapper/actions/workflows/ci.yml?query=branch%3Amaster)
1
+ # @xhmikosr/bin-wrapper [![npm version](https://img.shields.io/npm/v/@xhmikosr/bin-wrapper?logo=npm&logoColor=fff)](https://www.npmjs.com/package/@xhmikosr/bin-wrapper) [![CI Status](https://img.shields.io/github/actions/workflow/status/XhmikosR/bin-wrapper/ci.yml?branch=master&label=CI&logo=github)](https://github.com/XhmikosR/bin-wrapper/actions/workflows/ci.yml?query=branch%3Amaster)
2
2
 
3
3
  > Binary wrapper that makes your programs seamlessly available as local dependencies
4
4