vnu-jar
Version:
Provides the Nu Html Checker «vnu.jar» file
154 lines (141 loc) • 5.15 kB
JavaScript
;
const { spawnSync, execSync } = require('child_process');
const { mkdirSync, existsSync, createWriteStream } = require('fs');
const { join, dirname, parse } = require('path');
const { Readable } = require('stream');
const fetch = globalThis.fetch || require('node-fetch');
function findNearestNodeModules(startDir) {
let dir = startDir;
const { root } = parse(startDir);
while (true) {
const candidate = join(dir, 'node_modules');
if (existsSync(candidate)) {
return candidate;
}
if (dir === root) {
throw new Error('Could not find a node_modules directory.');
}
dir = dirname(dir);
}
}
const NODE_MODULES_DIR = findNearestNodeModules(__dirname);
const CACHE_DIR = join(NODE_MODULES_DIR, '.cache', 'vnu-jar', 'java');
const TEMURIN_VERSION = '17.0.17+10';
const TEMURIN_BASE_URL = `https://github.com/adoptium/temurin17-binaries/releases/download/jdk-${TEMURIN_VERSION}/`;
function getJavaVersion(javaPath) {
try {
const result = spawnSync(javaPath, ['-version'], { encoding: 'utf8' });
const versionOutput = result.stderr || result.stdout || '';
const match = versionOutput.match(/version "(\d+)(?:\.(\d+))?/);
if (match) {
const majorVersion = parseInt(match[1], 10);
return majorVersion;
}
} catch (err) {
return null;
}
return null;
}
function findSystemJava() {
const whichCmd = process.platform === 'win32' ? 'where' : 'which';
const result = spawnSync(whichCmd, ['java'], { encoding: 'utf8' });
if (result.status === 0 && result.stdout) {
const javaPath = result.stdout.split(/\r?\n/)[0].trim();
const version = getJavaVersion(javaPath);
if (version !== null && version >= 11) {
return javaPath;
}
}
return null;
}
function getPlatformArchiveName() {
let arch = process.arch;
let plat = 'linux';
if (process.platform === 'win32') {
plat = 'windows';
} else if (process.platform === 'darwin') {
plat = 'mac';
}
if (plat === 'mac' && arch === 'arm64') {
arch = 'aarch64';
}
const ext = process.platform === 'win32' ? 'zip' : 'tar.gz';
const version = TEMURIN_VERSION.replace('+', '_');
return `OpenJDK17U-jre_${arch}_${plat}_hotspot_${version}.${ext}`;
}
async function downloadJava() {
mkdirSync(CACHE_DIR, { recursive: true });
const archiveName = getPlatformArchiveName();
const url = TEMURIN_BASE_URL + archiveName;
const archivePath = join(CACHE_DIR, archiveName);
console.log(`Downloading Java 17 runtime from ${url}...`);
const res = await fetch(url);
if (!res.ok) {
throw new Error(`Failed to download Java: ${res.status} ${res.statusText}`);
}
await new Promise((resolve, reject) => {
const file = createWriteStream(archivePath);
Readable.fromWeb(res.body)
.pipe(file)
.on('finish', resolve)
.on('error', reject);
});
if (process.platform === 'win32') {
console.log('Extracting ZIP archive...');
const extractRes = spawnSync('powershell', [
'-NoProfile',
'-NonInteractive',
'-Command',
'Expand-Archive',
'-Force',
archivePath,
CACHE_DIR
], { stdio: 'inherit' });
if (extractRes.status !== 0) {
throw new Error('Failed to extract Java archive.');
}
} else {
console.log('Extracting tar.gz archive...');
const extractRes = spawnSync('tar', ['-xzf', archivePath, '-C', CACHE_DIR], { stdio: 'inherit' });
if (extractRes.status !== 0) {
throw new Error('Failed to extract Java archive.');
}
}
console.log('Local Java runtime now installed.');
}
function resolveLocalJavaExecutable() {
let javaPath = join(CACHE_DIR, `jdk-${TEMURIN_VERSION}-jre`, 'bin', 'java');
if (process.platform === 'win32') {
javaPath = join(CACHE_DIR, `jdk-${TEMURIN_VERSION}-jre`, 'bin', 'java.exe');
} else if (process.platform === 'darwin') {
javaPath = join(CACHE_DIR, `jdk-${TEMURIN_VERSION}-jre`, 'Contents', 'Home', 'bin', 'java');
}
if (!existsSync(javaPath)) {
throw new Error('Local Java runtime not found after installation.');
}
return javaPath;
}
async function ensureLocalJava() {
if (!existsSync(CACHE_DIR)) {
await downloadJava();
}
return resolveLocalJavaExecutable();
}
async function resolveJava() {
const javaPath = findSystemJava();
if (javaPath) return javaPath;
try {
return resolveLocalJavaExecutable();
} catch (err) {
console.error('Failed to resolve local Java runtime, falling back to ensureLocalJava():', err);
}
return ensureLocalJava();
}
if (require.main === module) {
void resolveJava().catch((err) => {
console.error('Failed to resolve Java runtime:', err && err.stack ? err.stack : String(err));
process.exit(1);
});
}
module.exports = { resolveJava };