maxcso
Version:
💿 maxcso binaries and wrapper for Node.js.
200 lines • 9.59 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { promisify } from 'node:util';
import fs from 'node:fs';
import which from 'which';
import child_process from 'node:child_process';
import { Mutex } from 'async-mutex';
import path from 'node:path';
import stream from 'node:stream';
import os from 'node:os';
import crypto from 'node:crypto';
export var MaxcsoBinaryPreference;
(function (MaxcsoBinaryPreference) {
MaxcsoBinaryPreference[MaxcsoBinaryPreference["PREFER_BUNDLED_BINARY"] = 1] = "PREFER_BUNDLED_BINARY";
MaxcsoBinaryPreference[MaxcsoBinaryPreference["PREFER_PATH_BINARY"] = 2] = "PREFER_PATH_BINARY";
})(MaxcsoBinaryPreference || (MaxcsoBinaryPreference = {}));
/**
* Code to find and interact with the `maxcso` binary.
*/
class MaxcsoBin {
static getBinPath(binaryPreference) {
return __awaiter(this, void 0, void 0, function* () {
if (this.MAXCSO_BIN) {
return this.MAXCSO_BIN;
}
return this.MAXCSO_BIN_MUTEX.runExclusive(() => __awaiter(this, void 0, void 0, function* () {
if (this.MAXCSO_BIN) {
return this.MAXCSO_BIN;
}
if ((binaryPreference !== null && binaryPreference !== void 0 ? binaryPreference : MaxcsoBinaryPreference.PREFER_BUNDLED_BINARY)
=== MaxcsoBinaryPreference.PREFER_BUNDLED_BINARY) {
const pathBundled = yield this.getBinPathBundled();
this.MAXCSO_BIN = pathBundled !== null && pathBundled !== void 0 ? pathBundled : (yield this.getBinPathExisting());
}
else {
const pathExisting = yield this.getBinPathExisting();
this.MAXCSO_BIN = pathExisting !== null && pathExisting !== void 0 ? pathExisting : (yield this.getBinPathBundled());
}
return this.MAXCSO_BIN;
}));
});
}
static getBinPathBundled() {
return __awaiter(this, void 0, void 0, function* () {
const bunPath = yield this.getBinPathBundledBun();
if (bunPath !== undefined) {
return bunPath;
}
try {
const maxcso = yield import(`@emmercm/maxcso-${process.platform}-${process.arch}`);
const prebuilt = maxcso.default;
try {
yield promisify(fs.stat)(prebuilt);
return prebuilt;
}
catch ( /* ignored */_a) { /* ignored */ }
}
catch ( /* ignored */_b) { /* ignored */ }
return undefined;
});
}
/**
* Look for maxcso binaries bundled with:
* `bun build --compile --asset-naming="[name].[ext]" maxcso`
*/
static getBinPathBundledBun() {
return __awaiter(this, void 0, void 0, function* () {
try {
const { embeddedFiles } = yield import('bun');
// Find all files that might be maxcso-related
const maxcsoBlob = embeddedFiles.find((blob) => {
// @ts-expect-error https://github.com/oven-sh/bun/issues/20700
const blobName = blob.name;
return blobName.toLowerCase().startsWith('maxcso');
});
if (maxcsoBlob !== undefined) {
// Create the temporary directory
const hash = crypto.createHash('md5');
yield maxcsoBlob.stream().pipeTo(new WritableStream({
write(chunk) {
hash.update(chunk);
},
}));
const temporaryDirectory = yield this.getTemporaryDirectory(`maxcso-${hash.digest('hex').slice(0, 7)}`);
// Find additional files that might be necessary
const dylibBlobs = embeddedFiles.filter((blob) => {
// @ts-expect-error https://github.com/oven-sh/bun/issues/20700
const blobName = blob.name;
return blobName.toLowerCase().endsWith('.dylib');
});
// Extract all files if necessary
const temporaryBlobs = yield Promise.all([maxcsoBlob, ...dylibBlobs].map((blob) => __awaiter(this, void 0, void 0, function* () {
// @ts-expect-error https://github.com/oven-sh/bun/issues/20700
const blobName = blob.name.replace(/-[\da-z]{8}\./, '.').replace(/\.+$/, '');
const temporaryBlob = path.join(temporaryDirectory, blobName);
try {
yield promisify(fs.stat)(temporaryBlob);
return temporaryBlob;
}
catch (_a) {
/* ignored */
}
const writableStream = stream.Writable.toWeb(fs.createWriteStream(temporaryBlob));
yield blob.stream().pipeTo(writableStream);
yield promisify(fs.chmod)(temporaryBlob, 0o755); // chmod +x
return temporaryBlob;
})));
return temporaryBlobs.find((temporaryBlob) => path.basename(temporaryBlob).startsWith('maxcso'));
}
}
catch ( /* ignored */_a) { /* ignored */ }
return undefined;
});
}
static getTemporaryDirectory(temporaryDirectoryBasename) {
return __awaiter(this, void 0, void 0, function* () {
const candidateDirectories = [
path.join(os.tmpdir(), temporaryDirectoryBasename),
path.join(process.cwd(), '.maxcso', temporaryDirectoryBasename),
path.join(os.homedir(), '.maxcso', temporaryDirectoryBasename),
];
/* eslint-disable no-await-in-loop */
for (const candidateDirectory of candidateDirectories) {
try {
try {
yield promisify(fs.stat)(candidateDirectory);
}
catch (_a) {
yield promisify(fs.mkdir)(candidateDirectory, { recursive: true });
}
const temporaryFile = path.join(candidateDirectory, temporaryDirectoryBasename);
yield promisify(fs.writeFile)(temporaryFile, temporaryFile);
yield promisify(fs.unlink)(temporaryFile);
return candidateDirectory;
}
catch (_b) {
/* ignored */
}
}
throw new Error("couldn't find a suitable temporary directory");
});
}
static getBinPathExisting() {
return __awaiter(this, void 0, void 0, function* () {
const resolved = yield which(process.platform === 'win32' ? 'maxcso.exe' : 'maxcso', { nothrow: true });
if (resolved) {
return resolved;
}
return undefined;
});
}
/**
* Run maxcso with some arguments.
*/
static run(arguments_, options) {
return __awaiter(this, void 0, void 0, function* () {
const maxcsoBin = yield this.getBinPath(options === null || options === void 0 ? void 0 : options.binaryPreference);
if (!maxcsoBin) {
throw new Error('maxcso not found');
}
return new Promise((resolve, reject) => {
const proc = child_process.spawn(maxcsoBin, arguments_, { windowsHide: true });
const chunks = [];
proc.stdout.on('data', (chunk) => {
if (options === null || options === void 0 ? void 0 : options.logStd) {
console.log(chunk.toString());
}
chunks.push(chunk);
});
proc.stderr.on('data', (chunk) => {
if (options === null || options === void 0 ? void 0 : options.logStd) {
console.error(chunk.toString());
}
chunks.push(chunk);
});
proc.on('close', (code) => {
const output = Buffer.concat(chunks).toString().trim();
if (code !== null && code !== 0) {
return reject(output);
}
return resolve(output);
});
proc.on('error', () => {
const output = Buffer.concat(chunks).toString().trim();
reject(output);
});
});
});
}
}
MaxcsoBin.MAXCSO_BIN_MUTEX = new Mutex();
export default MaxcsoBin;
//# sourceMappingURL=maxcsoBin.js.map