Created
October 25, 2024 14:57
-
-
Save maksimr/29168a6a7a4b8a48ff58cd6d3d8a1bc2 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// @ts-check | |
main(); | |
/** | |
* @returns {Promise<void>} | |
*/ | |
async function main() { | |
/**@type {{version?: string; output?: string; help?: boolean}}*/ | |
const options = {}; | |
for (const arg of process.argv.slice(2)) { | |
if (!arg.startsWith('--')) { | |
continue; | |
} | |
const i = process.argv.indexOf(arg); | |
const rawKey = arg.slice(2); | |
const [key, value] = arg.includes('=') ? rawKey.split('=') : [rawKey, process.argv[i + 1] ?? true]; | |
options[key] = value; | |
} | |
const defaultVersion = '4.13.2'; | |
if (options.help) { | |
console.log('Usage:'); | |
console.log(` [--version=<version>] Version of ANTLR (default: ${defaultVersion})`); | |
console.log(' [--output=<path>.jar] Output path for the jar file'); | |
return | |
} | |
const version = options.version || defaultVersion; | |
const antlrJarPath = options.output || require('path').join(require('os').tmpdir(), `antlr-${version}-complete.jar`); | |
const antlrJarUrl = `https://www.antlr.org/download/antlr-${version}-complete.jar` | |
await downloadAntlr({ | |
downloadUrl: antlrJarUrl, | |
outputFilePath: antlrJarPath | |
}); | |
console.log(antlrJarPath) | |
} | |
/** | |
* @param {{downloadUrl: string; outputFilePath: string; force?: Boolean}} param0 | |
* @returns {Promise<string>} | |
*/ | |
async function downloadAntlr({ | |
downloadUrl, | |
outputFilePath, | |
force = false, | |
}) { | |
if (!force && await exists(outputFilePath)) { | |
return outputFilePath; | |
} | |
await downloadFile(downloadUrl) | |
.then(async (response) => { | |
const path = require('path') | |
const installDir = path.dirname(outputFilePath); | |
await require('fs/promises').mkdir(installDir, { recursive: true }); | |
return new Promise((resolve, reject) => { | |
const progress = { | |
packageSize: parseInt(response.headers['content-length'] || '', 10), | |
dots: 0, | |
downloadedBytes: 0, | |
downloadPercentage: 0 | |
}; | |
console.log(`(${Math.ceil(progress.packageSize / 1024)} KB) `); | |
response.on('data', data => { | |
progress.downloadedBytes += data.length; | |
// Update status bar item with percentage | |
if (progress.packageSize > 0) { | |
let newPercentage = Math.ceil(100 * (progress.downloadedBytes / progress.packageSize)); | |
if (newPercentage !== progress.downloadPercentage) { | |
progress.downloadPercentage = newPercentage; | |
if (progress.downloadPercentage === 100) { | |
process.stdout.write('\n'); | |
} | |
} | |
// Update dots after package name in output console | |
let newDots = Math.ceil(progress.downloadPercentage / 5); | |
if (newDots > progress.dots) { | |
process.stdout.write('.'.repeat(newDots - progress.dots)); | |
progress.dots = newDots; | |
} | |
} | |
}); | |
const { createWriteStream } = require('fs'); | |
const tmpFile = createWriteStream(outputFilePath); | |
response.on('end', () => { | |
resolve(tmpFile.path); | |
}); | |
response.on('error', (/**@type {Error & {code?: string}}*/ err) => { | |
reject(new Error(`Response error: ${err.code || 'NONE'}`)); | |
}); | |
// Begin piping data from the response to the package file | |
response.pipe(tmpFile, { end: false }); | |
}); | |
}); | |
return outputFilePath; | |
} | |
/** | |
* @param {string} fileUrl | |
* @returns {Promise<import('http').IncomingMessage>} | |
*/ | |
function downloadFile(fileUrl) { | |
const options = getHttpClientOptions(fileUrl, undefined, false); | |
const httpClient = fileUrl.startsWith('http:') ? | |
require('http').request : | |
require('https').request; | |
return new Promise((resolve, reject) => { | |
const request = httpClient(options, response => { | |
if (response.statusCode === 301 || response.statusCode === 302) { | |
if (!response.headers.location) { | |
return reject(new Error('Redirect status code, but no location header')); | |
} | |
// Redirect - download from new location | |
return resolve( | |
downloadFile( | |
response.headers.location | |
) | |
); | |
} | |
if (response.statusCode !== 200) { | |
// Download failed - print error message | |
return reject(new Error(response.statusCode?.toString())); | |
} | |
resolve(response); | |
}); | |
request.on('error', (/**@type {Error & {code?: string}}*/ error) => { | |
reject(error); | |
}); | |
request.end(); | |
}); | |
} | |
/** | |
* @param {string} urlStr | |
* @param {string} [proxy] | |
* @param {boolean} [strictSSL] | |
* @param {string} [authorization] | |
*/ | |
function getHttpClientOptions(urlStr, proxy, strictSSL, authorization) { | |
const url = require('url').parse(urlStr); | |
const agent = getProxyAgent(urlStr, proxy, strictSSL); | |
/**@type {import('http').RequestOptions & import('https').RequestOptions}*/ | |
const options = { | |
host: url.hostname, | |
path: url.path, | |
agent: agent | |
}; | |
if (url.protocol === 'https:') { | |
options.rejectUnauthorized = isBoolean(strictSSL) ? strictSSL : true; | |
} | |
if (authorization) { | |
options.headers = Object.assign(options.headers || {}, { 'Proxy-Authorization': authorization }); | |
} | |
return options; | |
} | |
/** | |
* @param {string} requestUrl | |
* @param {string} [proxy] | |
* @param {boolean} [strictSSL] | |
* @returns {any} | |
*/ | |
function getProxyAgent(requestUrl, proxy, strictSSL) { | |
const proxyURL = proxy || getSystemProxyURL(requestUrl); | |
if (!proxyURL) { | |
return undefined; | |
} | |
if (!/^https?:/.test(requestUrl)) { | |
return undefined; | |
} | |
const ProxyAgent = requestUrl.startsWith('http:') ? | |
require('http-proxy-agent').HttpProxyAgent : | |
require('https-proxy-agent').HttpsProxyAgent | |
return new ProxyAgent(proxyURL, { | |
rejectUnauthorized: isBoolean(strictSSL) ? strictSSL : true | |
}); | |
/** | |
* @param {string} requestUrl | |
* @returns {string | undefined} | |
*/ | |
function getSystemProxyURL(requestUrl) { | |
if (requestUrl.startsWith('http:')) { | |
return process.env.HTTP_PROXY || process.env.http_proxy || undefined; | |
} else if (requestUrl.startsWith('https:')) { | |
return process.env.HTTPS_PROXY || process.env.https_proxy || process.env.HTTP_PROXY || process.env.http_proxy || undefined; | |
} | |
return undefined; | |
} | |
} | |
/** | |
* @param {any} obj | |
* @returns {obj is boolean} | |
*/ | |
function isBoolean(obj) { | |
return obj === true || obj === false; | |
} | |
async function exists(filePath) { | |
const fs = require('fs'); | |
return await fs.promises.access(filePath, fs.constants.F_OK) | |
.then(() => true) | |
.catch(() => false); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment