122 lines
3.6 KiB
TypeScript
122 lines
3.6 KiB
TypeScript
import * as plugins from './plugins.js';
|
|
import * as paths from './paths.js';
|
|
import { promises as fs } from 'fs';
|
|
import { platform } from 'os';
|
|
|
|
interface IDenoRelease {
|
|
name: string;
|
|
assets: IAsset[];
|
|
}
|
|
|
|
interface IAsset {
|
|
name: string;
|
|
browser_download_url: string;
|
|
}
|
|
|
|
export class DenoDownloader {
|
|
private denoBinaryPath: string | null = null;
|
|
|
|
private async getDenoDownloadUrl(): Promise<string> {
|
|
const osPlatform = platform();
|
|
const arch = process.arch;
|
|
let osPart: string;
|
|
|
|
switch (osPlatform) {
|
|
case 'darwin':
|
|
osPart = 'apple-darwin';
|
|
break;
|
|
case 'win32':
|
|
osPart = 'pc-windows-msvc';
|
|
break;
|
|
case 'linux':
|
|
osPart = 'unknown-linux-gnu';
|
|
break;
|
|
default:
|
|
throw new Error(`Unsupported platform: ${osPlatform}`);
|
|
}
|
|
|
|
const archPart = arch === 'x64' ? 'x86_64' : 'aarch64';
|
|
|
|
const releasesResponse = await fetch('https://api.github.com/repos/denoland/deno/releases/latest');
|
|
if (!releasesResponse.ok) {
|
|
throw new Error(`Failed to fetch Deno releases: ${releasesResponse.statusText}`);
|
|
}
|
|
const release: IDenoRelease = await releasesResponse.json();
|
|
|
|
const executableName = `deno-${archPart}-${osPart}.zip`;
|
|
const asset = release.assets.find(a => a.name === executableName);
|
|
|
|
if (!asset) {
|
|
throw new Error(`Deno release for ${osPlatform} (${arch}) not found.`);
|
|
}
|
|
|
|
return asset.browser_download_url;
|
|
}
|
|
|
|
private async downloadDeno(url: string, outputPath: string): Promise<void> {
|
|
const response = await fetch(url);
|
|
if (!response.ok) {
|
|
throw new Error(`Failed to download Deno: ${response.statusText}`);
|
|
}
|
|
const buffer = await response.arrayBuffer();
|
|
await fs.writeFile(outputPath, Buffer.from(buffer));
|
|
}
|
|
|
|
/**
|
|
* Get the path to the Deno binary after download
|
|
*/
|
|
public getDenoBinaryPath(): string | null {
|
|
return this.denoBinaryPath;
|
|
}
|
|
|
|
/**
|
|
* Download and extract Deno to the specified directory
|
|
* @param outputPath Path where the deno.zip will be downloaded
|
|
* @returns Path to the Deno binary
|
|
*/
|
|
public async download(outputPath: string = './deno.zip'): Promise<string> {
|
|
const fsInstance = new plugins.smartfs.SmartFs(new plugins.smartfs.SmartFsProviderNode());
|
|
const directory = plugins.path.dirname(outputPath);
|
|
const denoBinaryPath = plugins.path.join(directory, platform() === 'win32' ? 'deno.exe' : 'deno');
|
|
|
|
// Check if Deno is already downloaded
|
|
if (await fsInstance.file(denoBinaryPath).exists()) {
|
|
console.log(`Deno already exists at ${denoBinaryPath}`);
|
|
this.denoBinaryPath = denoBinaryPath;
|
|
return denoBinaryPath;
|
|
}
|
|
|
|
// Ensure the directory exists
|
|
await fsInstance.directory(directory).create();
|
|
|
|
// Download Deno
|
|
const url = await this.getDenoDownloadUrl();
|
|
await this.downloadDeno(url, outputPath);
|
|
console.log(`Deno downloaded successfully to ${outputPath}`);
|
|
|
|
// Extract the archive
|
|
console.log(`Extracting deno.zip to ${directory}`);
|
|
await plugins.smartarchive.SmartArchive.create()
|
|
.file(outputPath)
|
|
.extract(directory);
|
|
|
|
// Make the binary executable (Unix-like systems)
|
|
if (platform() !== 'win32') {
|
|
const smartshellInstance = new plugins.smartshell.Smartshell({
|
|
executor: 'bash'
|
|
});
|
|
await smartshellInstance.exec(`chmod +x "${denoBinaryPath}"`);
|
|
}
|
|
|
|
// Clean up the zip file
|
|
try {
|
|
await fsInstance.file(outputPath).delete();
|
|
} catch {
|
|
// Ignore cleanup errors
|
|
}
|
|
|
|
this.denoBinaryPath = denoBinaryPath;
|
|
return denoBinaryPath;
|
|
}
|
|
}
|