toml-test
Version:
This is a Node.js wrapper for [toml-test](https://github.com/BurntSushi/toml-test).
214 lines (185 loc) • 5.82 kB
JavaScript
import { createRequire } from 'node:module';
import { URL, fileURLToPath } from 'node:url';
import { createReadStream, createWriteStream, existsSync } from 'node:fs';
import * as Path from 'node:path';
import * as fs from 'node:fs/promises';
import { createGunzip } from 'node:zlib';
import { spawn } from 'node:child_process';
import fetch, { FetchError } from 'node-fetch';
import { HttpProxyAgent } from 'http-proxy-agent';
import { HttpsProxyAgent } from 'https-proxy-agent';
import { SocksProxyAgent } from 'socks-proxy-agent';
import ora from 'ora';
const DEFAULT_TOML_TEST_BINARY_HOST = 'https://github.com/BurntSushi/toml-test/releases/download';
const HELP_MESSAGE = `Please try the following solutions:
1. Set up a proxy
export HTTP_PROXY=http://proxy.example.com:1080
export HTTPS_PROXY=http://proxy.example.com:1080
export ALL_PROXY=socks5://proxy.example.com:1080
2. Set up a mirror
export TOML_TEST_BINARY_HOST=https://mirror.example.com/toml-test`;
export class TomlTest {
get binaryHost() {
return process.env.TOML_TEST_BINARY_HOST || process.env.npm_config_TOML_TEST_BINARY_HOST || DEFAULT_TOML_TEST_BINARY_HOST;
}
get version() {
const require = createRequire(import.meta.url);
const pkg = require('./package.json');
return pkg.tomlTestVersion;
}
get platform() {
if (process.platform === 'win32') {
return 'windows';
}
return process.platform;
}
get arch() {
if (process.arch === 'x64') {
return 'amd64';
}
return process.arch;
}
get binaryFilename() {
let name = `toml-test-v${this.version}-${this.platform}-${this.arch}`;
if (this.platform === 'windows') {
name += '.exe';
}
return name;
}
get compressedFilename() {
return `${this.binaryFilename}.gz`;
}
get url() {
return new URL(Path.join(this.binaryHost, `v${this.version}/${this.compressedFilename}`));
}
get distPath() {
const __dirname = Path.dirname(fileURLToPath(import.meta.url));
return Path.join(__dirname, 'dist');
}
get binaryFilePath() {
return Path.join(this.distPath, this.binaryFilename);
}
get compressedFilePath() {
return Path.join(this.distPath, this.compressedFilename);
}
async run() {
await this.ensureBinaryFileExists();
const argv = [...process.argv];
argv.shift();
argv.shift();
spawn(this.binaryFilePath, argv, { stdio: 'inherit' })
}
async ensureBinaryFileExists() {
if (existsSync(this.binaryFilePath)) {
return;
}
const spinner = ora('Downloading toml-test binary').start();
try {
await this.download();
spinner.succeed('Download tom-test binary success');
} catch(e) {
await fs.unlink(this.binaryFilePath).catch(_ => {});
await fs.unlink(this.compressedFilePath).catch(_ => {});
if (e instanceof FetchError) {
spinner.fail('Download toml-test binary failed');
console.log(HELP_MESSAGE);
} else {
console.error(e);
}
process.exit(1);
}
}
async download() {
const res = await this.createFetch(this.url);
await this.ensureDistExists();
const ws = createWriteStream(this.compressedFilePath);
const p = new Promise((resolve, reject) => {
res.body.on('error', reject);
ws.on('finish', resolve);
ws.on('error', reject);
});
res.body.pipe(ws);
await p;
await this.decompress();
await fs.chmod(this.binaryFilePath, 0o775);
}
async ensureDistExists() {
return await fs.mkdir(this.distPath).catch(_ => {});
}
async createFetch(url) {
const res = await fetch(url.href, { agent: this.getAgent(url), redirect: 'manual' });
if (res.status === 301 || res.status === 302) {
const locationURL = new URL(res.headers.get('location'), res.url);
return this.createFetch(locationURL);
}
if (!res.ok) {
throw new Error(`status: ${res.status}`);
}
return res;
}
async decompress() {
const rs = createReadStream(this.compressedFilePath);
const gunzip = createGunzip();
const ws = createWriteStream(this.binaryFilePath);
const p = new Promise((resolve, reject) => {
rs.on('error', reject);
gunzip.on('error', reject);
ws.on('error',reject);
ws.on('finish', resolve);
})
rs.pipe(gunzip).pipe(ws);
await p;
}
getAgent(url) {
let agent;
if (url.protocol === 'http:') {
agent = this.createHttpProxyAgent();
} else if (url.protocol === 'https:') {
agent = this.createHttpsProxyAgent();
}
if (!agent) {
agent = this.createAllProxyAgent(url.protocol);
}
return agent;
}
createHttpProxyAgent() {
const proxyUrl = process.env.http_proxy || process.env.HTTP_PROXY;
if (!proxyUrl) {
return null;
}
if (/^http/i.test(proxyUrl)) {
return new HttpProxyAgent(proxyUrl);
} else if (/^socks/i.test(proxyUrl)) {
return new SocksProxyAgent(proxyUrl);
}
return null;
}
createHttpsProxyAgent() {
const proxyUrl = process.env.https_proxy || process.env.HTTPS_PROXY;
if (!proxyUrl) {
return null;
}
if (/^http/i.test(proxyUrl)) {
return new HttpsProxyAgent(proxyUrl);
} else if (/^socks/i.test(proxyUrl)) {
return new SocksProxyAgent(proxyUrl);
}
return null;
}
createAllProxyAgent(protocol) {
const proxyUrl = process.env.all_proxy || process.env.ALL_PROXY;
if (!proxyUrl) {
return null;
}
if (/^http/i.test(proxyUrl)) {
if (protocol === 'http:') {
return new HttpProxyAgent(proxyUrl);
} else if (protocol === 'https:') {
return new HttpsProxyAgent(proxyUrl);
}
} else if (/^socks/i.test(proxyUrl)) {
return new SocksProxyAgent(proxyUrl);
}
return null;
}
}