Rewrite install

This commit is contained in:
Luis Blanco 2020-03-23 23:42:13 +03:00
parent 9bcce8ede3
commit dcccb8d4c0
3 changed files with 245 additions and 21 deletions

View File

@ -117,3 +117,25 @@ const stream = new WritableBuffer();
sourceStream.pipe(stream); sourceStream.pipe(stream);
sourceStream.on('end', () => useData(stream.get())); sourceStream.on('end', () => useData(stream.get()));
``` ```
## utils.js
* `read` - (async) Reads a whole file to string, NOT A Buffer.
* `write` - (async) Write a file.
* `copy` - (async) Copy a file.
* `exists` - (async) Check if a file/folder exists.
* `mkdir` - (async) Create an empty folder.
* `stat` - (async) Get status on a file.
* `isDir` - (async) Check if the path is a folder.
* `isFile` - (async) Check if the path is a file.
* `dirUp` - Cut the path one folder up.
* `ensuredir` - (async) Like `mkdir -p`, makes sure a directory exists.
* `copysafe` - (async) Copy a file, `dest` folder is created if needed.
* `readdir` - (async) Get file/folder names of the 1st level.
* `subdirs` - (async) Get folder paths (concatenated with input) of the 1st level.
* `subfiles` - (async) Get file paths (concatenated with input) of the 1st level.
* `traverse` - (async) Get all nested files recursively.
* `copyall` - (async) Copy a folder with all the contained files.
* `rmdir` - (async) Like `rm -rf`, removes everything recursively.
* `rm` - (async) Remove a file. Must be a file, not a folder. Just `fs.unlink`.

View File

@ -8,6 +8,7 @@ const fs = require('fs');
const AdmZip = require('adm-zip'); const AdmZip = require('adm-zip');
const { bin, platform } = require('.'); const { bin, platform } = require('.');
const { mkdir, rm } = require('./utils');
const protocols = { http, https }; const protocols = { http, https };
@ -20,11 +21,16 @@ const onError = msg => {
const zipPath = `${bin}/${bin}.zip`; const zipPath = `${bin}/${bin}.zip`;
const install = (url, count = 1) => { const install = async (url, count = 1) => {
try {
const proto = protocols[url.match(/^https?/)[0]];
const proto = protocols[url.match(/^https?/)[0]];
const request = proto.get(url, response => {
const response = await new Promise((res, rej) => {
const request = proto.get(url, response => res(response));
request.on('error', err => rej(err));
});
response.on('error', err => { throw err; });
// Handle redirects // Handle redirects
if ([301, 302, 303, 307].includes(response.statusCode)) { if ([301, 302, 303, 307].includes(response.statusCode)) {
@ -32,36 +38,37 @@ const install = (url, count = 1) => {
return install(response.headers.location, count + 1); return install(response.headers.location, count + 1);
} }
console.log(url); console.log(url);
return onError('Error: Too many redirects.'); throw new Error('Error: Too many redirects.');
} }
// Handle bad status // Handle bad status
if (response.statusCode !== 200) { if (response.statusCode !== 200) {
console.log(url); console.log(url);
return onError(`Response status was ${response.statusCode}`); throw new Error(`Response status was ${response.statusCode}`);
} }
response.on('error', err => onError(err.message)); await mkdir(bin);
fs.mkdirSync(bin); await new Promise((res, rej) => {
const zipWriter = fs.createWriteStream(zipPath); const zipWriter = fs.createWriteStream(zipPath);
zipWriter.on('error', err => onError(err.message)); zipWriter.on('error', err => rej(err));
response.pipe(zipWriter); zipWriter.on('finish', () => res());
response.pipe(zipWriter);
zipWriter.on('finish', () => {
const zip = new AdmZip(zipPath);
zip.extractAllTo(bin, true);
fs.unlinkSync(zipPath);
}); });
}); const zip = new AdmZip(zipPath);
zip.extractAllTo(bin, true);
request.on('error', err => onError(err.message));
await rm(zipPath);
} catch (ex) {
onError(ex.message);
}
}; };
module.exports = folder => { module.exports = folder => {
const url = `${folder}/${platform}.zip`; const url = `${folder}/${platform}.zip`;
install(url); install(url).then();
}; };

195
utils.js Normal file
View File

@ -0,0 +1,195 @@
'use strict';
const fs = require('fs');
// (async) Reads a whole file to string, NOT A Buffer
const read = name => new Promise(
(res, rej) => fs.readFile(
name,
(err, data) => (err ? rej(err) : res(data.toString()))
)
);
// (async) Write a file
const write = (name, text) => new Promise(
(res, rej) => fs.writeFile(name, text, err => (err ? rej(err) : res()))
);
// (async) Copy a file
const copy = async (src, dest) => {
try {
await new Promise(
(res, rej) => fs.copyFile(src, dest, err => (err ? rej(err) : res()))
);
} catch (e) {
if (e.code !== 'EBUSY') {
console.warn('WARNING\n', e);
}
}
};
// (async) Check if a file/folder exists
const exists = name => new Promise(
res => fs.access(
name,
fs.constants.F_OK,
err => res(err ? false : true)
)
);
// (async) Create an empty folder
const mkdir = async name => {
if (await exists(name)) {
return;
}
return new Promise(
(res, rej) => fs.mkdir(name, err => (err ? rej(err) : res()))
);
};
// (async) Get status on a file
const stat = name => new Promise(
(res, rej) => fs.stat(name, (err, stats) => (err ? rej(err) : res(stats)))
);
// (async) Check if the path is a folder
const isDir = async name => (await stat(name)).isDirectory();
// (async) Check if the path is a file
const isFile = async name => (await stat(name)).isFile();
// Cut the path one folder up
const dirUp = dir => dir.replace(/\\/g, '/').split('/').slice(0, -1).join('/');
// (async) Like `mkdir -p`, makes sure a directory exists
const ensuredir = async dir => {
if (await exists(dir) && await isDir(dir)) {
return;
}
await ensuredir(dirUp(dir));
await mkdir(dir);
};
// (async) Copy a file, `dest` folder is created if needed
const copysafe = async (src, dest) => {
await ensuredir(dirUp(dest));
await copy(src, dest);
};
// (async) Get file/folder names of the 1st level
const readdir = name => new Promise(
(res, rej) => fs.readdir(
name,
(err, dirents) => (err ? rej(err) : res(dirents))
)
);
// (async) Get folder paths (concatenated with input) of the 1st level
const subdirs = async name => {
const all = await readdir(name);
const mapped = await Promise.all(all.map(d => isDir(`${name}/${d}`)));
return all.filter((_, i) => mapped[i]);
};
// (async) Get file paths (concatenated with input) of the 1st level
const subfiles = async name => {
const all = await readdir(name);
const mapped = await Promise.all(all.map(d => isFile(`${name}/${d}`)));
return all.filter((_, i) => mapped[i]).map(f => `${name}/${f}`);
};
// (async) Get all nested files recursively
// Folder paths are omitted by default
// Order is: shallow-to-deep, each subdirectory lists dirs-then-files.
const traverse = async (name, showDirs = false) => {
const dirs = [];
const stack = [name];
while (stack.length) {
const dir = stack.pop();
dirs.push(dir);
(await subdirs(dir)).forEach(d => stack.push(`${dir}/${d}`));
}
return (showDirs ? dirs : []).concat(
...(await Promise.all(dirs.map(subfiles)))
);
};
// (async) Copy a folder with all the contained files
const copyall = async (src, dest) => {
const files = (await traverse(src, true)).reverse();
while (files.length) {
const target = files.pop();
const dir = await isDir(target);
if (dir) {
await mkdir(target.replace(src, dest));
} else {
await copy(target, target.replace(src, dest));
}
}
};
// (async) Like `rm -rf`, removes everything recursively
const rmdir = async name => {
if ( ! await exists(name) ) {
return;
}
const paths = await traverse(name, true);
while (paths.length) {
const target = paths.pop();
const dir = await isDir(target);
await new Promise(
(res, rej) => fs[dir ? 'rmdir' : 'unlink'](
target,
err => (err ? rej(err) : res())
)
);
}
};
// (async) Remove a file. Must be a file, not a folder. Just `fs.unlink`.
const rm = async name => {
if ( ! await exists(name) ) {
return;
}
await new Promise(
(res, rej) => fs.unlink(name, err => (err ? rej(err) : res()))
);
};
module.exports = {
read,
write,
copy,
exists,
mkdir,
stat,
isDir,
isFile,
dirUp,
ensuredir,
copysafe,
readdir,
subdirs,
subfiles,
traverse,
copyall,
rmdir,
rm,
};