@xhmikosr/bin-wrapper 5.0.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.
Files changed (4) hide show
  1. package/index.js +186 -0
  2. package/license +9 -0
  3. package/package.json +60 -0
  4. package/readme.md +133 -0
package/index.js ADDED
@@ -0,0 +1,186 @@
1
+ import {promises as fs} from 'node:fs';
2
+ import path from 'node:path';
3
+ import binCheck from 'bin-check';
4
+ import binVersionCheck from 'bin-version-check';
5
+ import download from '@xhmikosr/downloader';
6
+ import osFilterObject from 'os-filter-obj';
7
+
8
+ /**
9
+ * Initialize a new `BinWrapper`
10
+ *
11
+ * @param {Object} options
12
+ * @api public
13
+ */
14
+ export default class BinWrapper {
15
+ constructor(options = {}) {
16
+ this.options = options;
17
+
18
+ if (this.options.strip <= 0) {
19
+ this.options.strip = 0;
20
+ } else if (!this.options.strip) {
21
+ this.options.strip = 1;
22
+ }
23
+ }
24
+
25
+ /**
26
+ * Get or set files to download
27
+ *
28
+ * @param {String} src
29
+ * @param {String} os
30
+ * @param {String} arch
31
+ * @api public
32
+ */
33
+ src(src, os, arch) {
34
+ if (arguments.length === 0) {
35
+ return this._src;
36
+ }
37
+
38
+ this._src = this._src || [];
39
+ this._src.push({
40
+ url: src,
41
+ os,
42
+ arch,
43
+ });
44
+
45
+ return this;
46
+ }
47
+
48
+ /**
49
+ * Get or set the destination
50
+ *
51
+ * @param {String} dest
52
+ * @api public
53
+ */
54
+ dest(dest) {
55
+ if (arguments.length === 0) {
56
+ return this._dest;
57
+ }
58
+
59
+ this._dest = dest;
60
+ return this;
61
+ }
62
+
63
+ /**
64
+ * Get or set the binary
65
+ *
66
+ * @param {String} bin
67
+ * @api public
68
+ */
69
+ use(bin) {
70
+ if (arguments.length === 0) {
71
+ return this._use;
72
+ }
73
+
74
+ this._use = bin;
75
+ return this;
76
+ }
77
+
78
+ /**
79
+ * Get or set a semver range to test the binary against
80
+ *
81
+ * @param {String} range
82
+ * @api public
83
+ */
84
+ version(range) {
85
+ if (arguments.length === 0) {
86
+ return this._version;
87
+ }
88
+
89
+ this._version = range;
90
+ return this;
91
+ }
92
+
93
+ /**
94
+ * Get path to the binary
95
+ *
96
+ * @api public
97
+ */
98
+ path() {
99
+ return path.join(this.dest(), this.use());
100
+ }
101
+
102
+ /**
103
+ * Run
104
+ *
105
+ * @param {Array} cmd
106
+ * @api public
107
+ */
108
+ run(cmd = ['--version']) {
109
+ return this.findExisting().then(() => {
110
+ if (this.options.skipCheck) {
111
+ return;
112
+ }
113
+
114
+ return this.runCheck(cmd);
115
+ });
116
+ }
117
+
118
+ /**
119
+ * Run binary check
120
+ *
121
+ * @param {Array} cmd
122
+ * @api private
123
+ */
124
+ runCheck(cmd) {
125
+ return binCheck(this.path(), cmd).then(works => {
126
+ if (!works) {
127
+ throw new Error(`The \`${this.path()}\` binary doesn't seem to work correctly`);
128
+ }
129
+
130
+ if (this.version()) {
131
+ return binVersionCheck(this.path(), this.version());
132
+ }
133
+ });
134
+ }
135
+
136
+ /**
137
+ * Find existing files
138
+ *
139
+ * @api private
140
+ */
141
+ findExisting() {
142
+ return fs.stat(this.path()).catch(error => {
143
+ if (error && error.code === 'ENOENT') {
144
+ return this.download();
145
+ }
146
+
147
+ throw error;
148
+ });
149
+ }
150
+
151
+ /**
152
+ * Download files
153
+ *
154
+ * @api private
155
+ */
156
+ download() {
157
+ const files = osFilterObject(this.src() || []);
158
+
159
+ if (files.length === 0) {
160
+ return Promise.reject(new Error('No binary found matching your system. It\'s probably not supported.'));
161
+ }
162
+
163
+ const urls = [];
164
+ for (const file of files) {
165
+ urls.push(file.url);
166
+ }
167
+
168
+ return Promise.all(urls.map(url => download(url, this.dest(), {
169
+ extract: true,
170
+ strip: this.options.strip,
171
+ }))).then(result => {
172
+ const resultingFiles = result.flatMap((item, index) => {
173
+ if (Array.isArray(item)) {
174
+ return item.map(file => file.path);
175
+ }
176
+
177
+ const parsedUrl = new URL(files[index].url);
178
+ const parsedPath = path.parse(parsedUrl.pathname);
179
+
180
+ return parsedPath.base;
181
+ });
182
+
183
+ return Promise.all(resultingFiles.map(fileName => fs.chmod(path.join(this.dest(), fileName), 0o755)));
184
+ });
185
+ }
186
+ }
package/license ADDED
@@ -0,0 +1,9 @@
1
+ MIT License
2
+
3
+ Copyright (c) Kevin Mårtensson <kevinmartensson@gmail.com>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "@xhmikosr/bin-wrapper",
3
+ "version": "5.0.0",
4
+ "description": "Binary wrapper that makes your programs seamlessly available as local dependencies",
5
+ "license": "MIT",
6
+ "repository": "XhmikosR/bin-wrapper",
7
+ "publishConfig": {
8
+ "access": "public"
9
+ },
10
+ "author": {
11
+ "name": "Kevin Mårtensson",
12
+ "email": "kevinmartensson@gmail.com",
13
+ "url": "https://github.com/kevva"
14
+ },
15
+ "engines": {
16
+ "node": "^12.20.0 || ^14.14.0 || >=16.0.0"
17
+ },
18
+ "scripts": {
19
+ "ava": "ava",
20
+ "xo": "xo",
21
+ "test": "npm run xo && npm run ava",
22
+ "test-ci": "npm run xo && c8 ava"
23
+ },
24
+ "main": "index.js",
25
+ "type": "module",
26
+ "exports": {
27
+ ".": "./index.js"
28
+ },
29
+ "files": [
30
+ "index.js"
31
+ ],
32
+ "keywords": [
33
+ "bin",
34
+ "check",
35
+ "local",
36
+ "wrapper"
37
+ ],
38
+ "c8": {
39
+ "reporter": [
40
+ "lcovonly",
41
+ "text"
42
+ ]
43
+ },
44
+ "dependencies": {
45
+ "@xhmikosr/downloader": "^9.0.0",
46
+ "bin-check": "^4.1.0",
47
+ "bin-version-check": "^5.0.0",
48
+ "os-filter-obj": "^2.0.0"
49
+ },
50
+ "devDependencies": {
51
+ "ava": "^4.3.0",
52
+ "c8": "^7.11.3",
53
+ "executable": "^4.1.1",
54
+ "nock": "^13.2.6",
55
+ "path-exists": "^5.0.0",
56
+ "rimraf": "^3.0.2",
57
+ "tempy": "^2.0.0",
58
+ "xo": "^0.49.0"
59
+ }
60
+ }
package/readme.md ADDED
@@ -0,0 +1,133 @@
1
+ # bin-wrapper [![CI](https://github.com/XhmikosR/bin-wrapper/actions/workflows/ci.yml/badge.svg)](https://github.com/XhmikosR/bin-wrapper/actions/workflows/ci.yml)
2
+
3
+ > Binary wrapper that makes your programs seamlessly available as local dependencies
4
+
5
+
6
+ ## Install
7
+
8
+ ```sh
9
+ npm install bin-wrapper
10
+ ```
11
+
12
+
13
+ ## Usage
14
+
15
+ ```js
16
+ import path from 'node:path';
17
+ import BinWrapper from 'bin-wrapper';
18
+
19
+ const base = 'https://github.com/imagemin/gifsicle-bin/raw/main/vendor';
20
+ const bin = new BinWrapper()
21
+ .src(`${base}/macos/gifsicle`, 'darwin')
22
+ .src(`${base}/linux/x64/gifsicle`, 'linux', 'x64')
23
+ .src(`${base}/win/x64/gifsicle.exe`, 'win32', 'x64')
24
+ .dest(path.join('vendor'))
25
+ .use(process.platform === 'win32' ? 'gifsicle.exe' : 'gifsicle')
26
+ .version('>=1.71');
27
+
28
+ (async () => {
29
+ await bin.run(['--version']);
30
+ console.log('gifsicle is working');
31
+ })();
32
+ ```
33
+
34
+ Get the path to your binary with `bin.path()`:
35
+
36
+ ```js
37
+ console.log(bin.path());
38
+ //=> 'path/to/vendor/gifsicle'
39
+ ```
40
+
41
+
42
+ ## API
43
+
44
+ ### `new BinWrapper(options)`
45
+
46
+ Creates a new `BinWrapper` instance.
47
+
48
+ #### options
49
+
50
+ Type: `Object`
51
+
52
+ ##### skipCheck
53
+
54
+ * Type: `boolean`
55
+ * Default: `false`
56
+
57
+ Whether to skip the binary check or not.
58
+
59
+ ##### strip
60
+
61
+ * Type: `number`
62
+ * Default: `1`
63
+
64
+ Strip a number of leading paths from file names on extraction.
65
+
66
+ ### .src(url, [os], [arch])
67
+
68
+ Adds a source to download.
69
+
70
+ #### url
71
+
72
+ Type: `string`
73
+
74
+ Accepts a URL pointing to a file to download.
75
+
76
+ #### os
77
+
78
+ Type: `string`
79
+
80
+ Tie the source to a specific OS.
81
+
82
+ #### arch
83
+
84
+ Type: `string`
85
+
86
+ Tie the source to a specific arch.
87
+
88
+ ### .dest(destination)
89
+
90
+ #### destination
91
+
92
+ Type: `string`
93
+
94
+ Accepts a path which the files will be downloaded to.
95
+
96
+ ### .use(binary)
97
+
98
+ #### binary
99
+
100
+ Type: `string`
101
+
102
+ Define which file to use as the binary.
103
+
104
+ ### .path()
105
+
106
+ Returns the full path to your binary.
107
+
108
+ ### .version(range)
109
+
110
+ #### range
111
+
112
+ Type: `string`
113
+
114
+ Define a [semver range](https://github.com/npm/node-semver#ranges) to check
115
+ the binary against.
116
+
117
+ ### .run([arguments])
118
+
119
+ Runs the search for the binary. If no binary is found it will download the file
120
+ using the URL provided in `.src()`.
121
+
122
+ #### arguments
123
+
124
+ * Type: `Array`
125
+ * Default: `['--version']`
126
+
127
+ Command to run the binary with. If it exits with code `0` it means that the
128
+ binary is working.
129
+
130
+
131
+ ## License
132
+
133
+ MIT © [Kevin Mårtensson](http://kevinmartensson.com)