Skip to content

Commit

Permalink
feat: lazily download deno binary for publish
Browse files Browse the repository at this point in the history
  • Loading branch information
marvinhagemeister committed Feb 27, 2024
1 parent 252c5d5 commit ac402d3
Show file tree
Hide file tree
Showing 7 changed files with 55 additions and 54 deletions.
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
node_modules/
dist/
dist-esm/
dist-scripts/
.download/
*.log
*.tgz
Expand Down
6 changes: 2 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@
"test": "mocha -r ts-node/register --extensions ts,tsx --timeout 30000 --watch-files src,test 'test/**/*.test.ts'",
"cli": "ts-node src/bin.ts",
"build": "rimraf dist dist-esm && tsc && tsc -p tsconfig.esm.json",
"prepublishOnly": "tsc",
"install": "node scripts/postinstall.js"
"prepublishOnly": "tsc"
},
"keywords": [
"install",
Expand All @@ -36,8 +35,7 @@
"license": "MIT",
"files": [
"dist/",
"dist-esm/",
"scripts/"
"dist-esm/"
],
"devDependencies": {
"@types/mocha": "^10.0.6",
Expand Down
9 changes: 2 additions & 7 deletions src/bin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,15 +157,10 @@ if (args.length === 0) {
await remove(packages, { pkgManagerName });
});
} else if (cmd === "publish") {
const binPath = path.join(
__dirname,
"..",
".download",
process.platform === "win32" ? "deno.exe" : "deno"
);
const binFolder = path.join(__dirname, "..", ".download");
run(() =>
publish(process.cwd(), {
binPath,
binFolder,
dryRun: options.values["dry-run"] ?? false,
allowSlowTypes: options.values["allow-slow-types"] ?? false,
token: options.values.token,
Expand Down
18 changes: 15 additions & 3 deletions src/commands.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import * as path from "node:path";
import * as fs from "node:fs";
import * as kl from "kolorist";
import { JsrPackage, exec } from "./utils";
import { JsrPackage, exec, fileExists } from "./utils";
import { Bun, PkgManagerName, getPkgManager } from "./pkg_manager";
import { downloadDeno } from "./download";

const NPMRC_FILE = ".npmrc";
const BUNFIG_FILE = "bunfig.toml";
Expand Down Expand Up @@ -94,13 +95,24 @@ export async function remove(packages: JsrPackage[], options: BaseOptions) {
}

export interface PublishOptions {
binPath: string;
binFolder: string;
dryRun: boolean;
allowSlowTypes: boolean;
token: string | undefined;
}

export async function publish(cwd: string, options: PublishOptions) {
// Check if deno executable is available, download it if not.
const binPath = path.join(
options.binFolder,
process.platform === "win32" ? "deno.exe" : "deno"
);

if (!(await fileExists(binPath))) {
await downloadDeno(binPath);
}

// Ready to publish now!
const args = [
"publish",
"--unstable-bare-node-builtins",
Expand All @@ -109,5 +121,5 @@ export async function publish(cwd: string, options: PublishOptions) {
if (options.dryRun) args.push("--dry-run");
if (options.allowSlowTypes) args.push("--allow-slow-types");
if (options.token) args.push("--token", options.token);
await exec(options.binPath, args, cwd);
await exec(binPath, args, cwd);
}
71 changes: 34 additions & 37 deletions scripts/postinstall.js → src/download.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,29 @@
const os = require("node:os");
const fs = require("node:fs");
const path = require("node:path");
const util = require("node:util");
const stream = require("node:stream");
const kl = require("kolorist");
const StreamZip = require("node-stream-zip");
import * as os from "node:os";
import * as fs from "node:fs";
import * as path from "node:path";
import * as util from "node:util";
import * as stream from "node:stream";
import * as kl from "kolorist";
import * as StreamZip from "node-stream-zip";

const streamFinished = util.promisify(stream.finished);

const DENO_CANARY_INFO_URL =
"https://dl.deno.land/canary-latest.txt";
const DENO_CANARY_INFO_URL = "https://dl.deno.land/canary-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
/** @type {Record<string, string>} */
const FILENAMES = {
const FILENAMES: Record<string, string> = {
"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",
};

/** @returns {Promise<{url: string, filename: string}>} */
async function getDenoDownloadUrl() {
async function getDenoDownloadUrl(): Promise<{
url: string;
filename: string;
}> {
const key = `${process.platform} ${os.arch()}`;
if (!(key in FILENAMES)) {
throw new Error(`Unsupported platform: ${key}`);
Expand All @@ -47,11 +47,11 @@ async function getDenoDownloadUrl() {
};
}

(async () => {
export async function downloadDeno(binPath: string): Promise<void> {
const info = await getDenoDownloadUrl();
const binFolder = path.dirname(binPath);

const targetPath = path.join(__dirname, "..", ".download");
await fs.promises.mkdir(targetPath, { recursive: true });
await fs.promises.mkdir(binFolder, { recursive: true });

const res = await fetch(info.url);
const contentLen = Number(res.headers.get("content-length") ?? Infinity);
Expand All @@ -63,38 +63,37 @@ async function getDenoDownloadUrl() {

await withProgressBar(
async (tick) => {
const tmpFile = path.join(targetPath, info.filename + ".part");
const tmpFile = path.join(binFolder, info.filename + ".part");
const writable = fs.createWriteStream(tmpFile, "utf-8");

for await (const chunk of streamToAsyncIterable(res.body)) {
for await (const chunk of streamToAsyncIterable(res.body!)) {
tick(chunk.length);
writable.write(chunk);
}

writable.end();
await streamFinished(writable);
const file = path.join(targetPath, info.filename);
const file = path.join(binFolder, info.filename);
await fs.promises.rename(tmpFile, file);

const zip = new StreamZip.async({ file });
await zip.extract(null, targetPath);
await zip.extract(null, binFolder);
await zip.close();

const deno = path.join(
targetPath,
process.platform === "win32" ? "deno.exe" : "deno"
);
await fs.promises.chmod(deno, 493);
// Mark as executable
await fs.promises.chmod(binPath, 493);

// Delete downloaded file
await fs.promises.rm(file);
},
{ max: contentLen }
);
})();
}

/** @type {<T>(fn: (tick: (n: number) => void) => Promise<T>, options: {max: number}) => Promise<T>} */
async function withProgressBar(fn, options) {
async function withProgressBar<T>(
fn: (tick: (n: number) => void) => Promise<T>,
options: { max: number }
): Promise<T> {
let current = 0;
let start = Date.now();
let passed = 0;
Expand Down Expand Up @@ -132,8 +131,7 @@ async function withProgressBar(fn, options) {
}
}, 16);

/** @type {(n: number) => void} */
const tick = (n) => {
const tick = (n: number) => {
current += n;
printStatus();
};
Expand All @@ -146,8 +144,9 @@ async function withProgressBar(fn, options) {
return res;
}

/** @type {<T>(stream: ReadableStream<T>) => AsyncIterable<T>} */
async function* streamToAsyncIterable(stream) {
async function* streamToAsyncIterable<T>(
stream: ReadableStream<T>
): AsyncIterable<T> {
const reader = stream.getReader();
try {
while (true) {
Expand All @@ -160,8 +159,7 @@ async function* streamToAsyncIterable(stream) {
}
}

/** @type {(bytes: number, digists?: number) => string} */
function humanFileSize(bytes, digits = 1) {
function humanFileSize(bytes: number, digits = 1): string {
const thresh = 1024;

if (Math.abs(bytes) < thresh) {
Expand All @@ -183,9 +181,8 @@ function humanFileSize(bytes, digits = 1) {
return `${bytes.toFixed(digits)} ${units[u]}`;
}

/** @type {(fn: () => void, delay: number) => () => void} */
function throttle(fn, delay) {
let timer = null;
function throttle(fn: () => void, delay: number): () => void {
let timer: NodeJS.Timeout | null = null;

return () => {
if (timer === null) {
Expand Down
2 changes: 1 addition & 1 deletion src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export class JsrPackage {
}
}

async function fileExists(file: string): Promise<boolean> {
export async function fileExists(file: string): Promise<boolean> {
try {
const stat = await fs.promises.stat(file);
return stat.isFile();
Expand Down
2 changes: 1 addition & 1 deletion test/commands.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -294,5 +294,5 @@ describe("publish", () => {

await runJsr(["publish", "--dry-run", "--token", "dummy-token"], dir);
});
});
}).timeout(600000);
});

0 comments on commit ac402d3

Please sign in to comment.