UNPKG

maxcso

Version:

💿 maxcso binaries and wrapper for Node.js.

200 lines • 9.59 kB
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