jsr
Version:
jsr.io package manager for node
183 lines • 6.93 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.downloadDeno = exports.getDenoDownloadUrl = void 0;
// Copyright 2024 the JSR authors. MIT license.
const os = __importStar(require("node:os"));
const fs = __importStar(require("node:fs"));
const path = __importStar(require("node:path"));
const util = __importStar(require("node:util"));
const stream = __importStar(require("node:stream"));
const kl = __importStar(require("kolorist"));
const StreamZip = __importStar(require("node-stream-zip"));
const streamFinished = util.promisify(stream.finished);
const DENO_CANARY_INFO_URL = "https://dl.deno.land/canary-latest.txt";
const DENO_RELEASE_INFO_URL = "https://dl.deno.land/release-latest.txt";
// Example: https://github.com/denoland/deno/releases/download/v1.41.0/deno-aarch64-apple-darwin.zip
// Example: https://dl.deno.land/canary/d722de886b85093eeef08d1e9fd6f3193405762d/deno-aarch64-apple-darwin.zip
const FILENAMES = {
"darwin arm64": "deno-aarch64-apple-darwin",
"darwin x64": "deno-x86_64-apple-darwin",
"linux arm64": "deno-aarch64-unknown-linux-gnu",
"linux x64": "deno-x86_64-unknown-linux-gnu",
"win32 x64": "deno-x86_64-pc-windows-msvc",
};
async function getDenoDownloadUrl(canary) {
const key = `${process.platform} ${os.arch()}`;
if (!(key in FILENAMES)) {
throw new Error(`Unsupported platform: ${key}`);
}
const name = FILENAMES[key];
const filename = name + ".zip";
const url = canary ? DENO_CANARY_INFO_URL : DENO_RELEASE_INFO_URL;
const res = await fetch(url);
if (!res.ok) {
await res.body?.cancel();
throw new Error(`${res.status}: Unable to retrieve ${canary ? "canary" : "release"} version information from ${url}.`);
}
const version = (await res.text()).trim();
return {
canary,
url: canary
? `https://dl.deno.land/canary/${decodeURI(version)}/${filename}`
: `https://dl.deno.land/release/${decodeURI(version)}/${filename}`,
filename,
version: version,
};
}
exports.getDenoDownloadUrl = getDenoDownloadUrl;
async function downloadDeno(binPath, info) {
const binFolder = path.dirname(binPath);
await fs.promises.mkdir(binFolder, { recursive: true });
const res = await fetch(info.url);
const contentLen = Number(res.headers.get("content-length") ?? Infinity);
if (res.body == null) {
throw new Error(`Unexpected empty body`);
}
console.log(`Downloading JSR ${info.canary ? "canary" : "release"} binary...`);
await withProgressBar(async (tick) => {
const tmpFile = path.join(binFolder, info.filename + ".part");
const writable = fs.createWriteStream(tmpFile, "utf-8");
for await (const chunk of streamToAsyncIterable(res.body)) {
tick(chunk.length);
writable.write(chunk);
}
writable.end();
await streamFinished(writable);
const file = path.join(binFolder, info.filename);
await fs.promises.rename(tmpFile, file);
const zip = new StreamZip.async({ file });
await zip.extract(null, binFolder);
await zip.close();
// Mark as executable
await fs.promises.chmod(binPath, 493);
// Delete downloaded file
await fs.promises.rm(file);
}, { max: contentLen });
}
exports.downloadDeno = downloadDeno;
async function withProgressBar(fn, options) {
let current = 0;
let start = Date.now();
let passed = 0;
let logged = false;
const printStatus = throttle(() => {
passed = Date.now() - start;
const minutes = String(Math.floor(passed / 1000 / 60)).padStart(2, "0");
const seconds = String(Math.floor(passed / 1000) % 60).padStart(2, "0");
const time = `[${minutes}:${seconds}]`;
const stats = `${humanFileSize(current)}/${humanFileSize(options.max)}`;
const width = process.stdout.columns;
let s = time;
if (width - time.length - stats.length + 4 > 10) {
const barLength = Math.min(width, 50);
const percent = Math.floor((100 / options.max) * current);
const bar = "#".repeat((barLength / 100) * percent) + ">";
const remaining = kl.blue("-".repeat(Math.max(barLength - bar.length, 0)));
s += ` [${kl.cyan(bar)}${remaining}] `;
}
s += kl.dim(stats);
if (process.stdout.isTTY) {
if (logged) {
process.stdout.write("\r\x1b[K");
}
logged = true;
process.stdout.write(s);
}
}, 16);
const tick = (n) => {
current += n;
printStatus();
};
const res = await fn(tick);
if (process.stdout.isTTY) {
process.stdout.write("\n");
}
else {
console.log("Download completed");
}
return res;
}
async function* streamToAsyncIterable(stream) {
const reader = stream.getReader();
try {
while (true) {
const { done, value } = await reader.read();
if (done)
return;
yield value;
}
}
finally {
reader.releaseLock();
}
}
function humanFileSize(bytes, digits = 1) {
const thresh = 1024;
if (Math.abs(bytes) < thresh) {
return bytes + " B";
}
const units = ["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"];
let u = -1;
const r = 10 ** digits;
do {
bytes /= thresh;
++u;
} while (Math.round(Math.abs(bytes) * r) / r >= thresh &&
u < units.length - 1);
return `${bytes.toFixed(digits)} ${units[u]}`;
}
function throttle(fn, delay) {
let timer = null;
return () => {
if (timer === null) {
fn();
timer = setTimeout(() => {
timer = null;
}, delay);
}
};
}
//# sourceMappingURL=download.js.map