UNPKG

@reforged/maker-appimage

Version:

An AppImage maker implementation for the Electron Forge.

134 lines 5.37 kB
import EventEmitter from "events"; import { existsSync } from "fs"; import { readFile } from "fs/promises"; import { execFileSync, execFile } from "child_process"; import { coerce } from "semver"; ; // FIXME: Library considerations? Should we make for it separate module? export function generateDesktop(desktopEntry, actions) { function toEscapeSeq(string) { if (typeof string === "string") return string .replaceAll(/\\(?!["`trn])/g, "\\\\") .replaceAll("`", "\\`") .replaceAll("\t", "\\t") .replaceAll("\r", "\\r") .replaceAll("\n", "\\n"); return string; } const template = { desktop: [], actions: [] }; let actionsKey = null; template.desktop.push('[Desktop Entry]'); for (const entry of Object.entries(desktopEntry)) if (entry[0] !== "Actions" && entry[1] !== undefined && entry[1] !== null) { if (Array.isArray(entry[1])) entry[1] = entry[1].length ? entry[1].join(";") + ";" : ""; template.desktop.push(entry.map(v => toEscapeSeq(v)).join('=')); } if (actions) for (const [name, record] of Object.entries(actions)) if (/[a-zA-Z]/.test(name)) { actionsKey === null ? actionsKey = name : actionsKey += ";" + name; template.actions.push('\n[Desktop Action ' + name + ']'); for (const entry of Object.entries(record)) if (entry[1] !== undefined && entry[1] !== null) template.actions.push(entry.map(v => toEscapeSeq(v)).join('=')); } if (actionsKey) template.desktop.push("Actions=" + actions); return template.desktop.join('\n') + '\n' + template.actions.join('\n'); } /** * A wrapper for `mksquashfs` binary. * * @returns An event used to watch for `mksquashfs` changes, including the job progress (in percent – as float number). */ export function mkSquashFs(...argv) { let lastProgress = 0, stderrCollector = ""; const event = new EventEmitter(), { PATH, SOURCE_DATE_EPOCH } = process.env, { stderr, stdout } = execFile("mksquashfs", argv, { encoding: "utf-8", windowsHide: true, env: { PATH, SOURCE_DATE_EPOCH } }).once("close", (...args) => event.emit("close", ...args, stderrCollector ? undefined : stderrCollector)).on("error", (error) => event.emit("error", error)); stderr?.on("data", (chunk) => { switch (chunk.constructor) { case String: stderrCollector += chunk; break; default: throw new TypeError(`Unresolved chunk of type '${chunk?.constructor.name ?? typeof chunk}'.`); } }); stdout?.on("data", (chunk) => { if (chunk.constructor !== String) return; const progress = chunk.match(/\] [0-9/]+ ([0-9]+)%/)?.[1]; if (progress === undefined) return; const progInt = parseInt(progress, 10); if (progInt >= 0 && progInt <= 100 && progInt !== lastProgress && event.emit("progress", progInt / 100)) lastProgress = progInt; }); return event; } /** * Returns the version of `mksquashfs` binary, as `SemVer` value. * * Under the hood, it executes `mksquashfs` with `-version`, parses * the `stdout` and tries to coerce it to `SemVer`. */ export function getSquashFsVer() { let output = execFileSync("mksquashfs", ["-version"], { encoding: "utf8", timeout: 3000, maxBuffer: 768, windowsHide: true, env: { PATH: process.env["PATH"] } }).split('\n')[0]; if (output === undefined) throw new TypeError("Unable to parse '-version': first line read error."); output = /(?<=version )[0-9.]+/.exec(output)?.[0]; if (output === undefined) throw new TypeError("Unable to parse '-version': number not found."); output = coerce(output); if (output === null) throw new Error(`Unable to coerce string '${output}' to SemVer.`); return output; } ; /** * Concatenates files and/or buffers into a new buffer. */ export async function joinFiles(...filesAndBuffers) { const buffArr = []; for (const path of filesAndBuffers) // Convert anything to Uint8Array as buffer representation if (path instanceof Object.getPrototypeOf(Uint8Array)) buffArr.push(Promise.resolve(new Uint8Array(path.buffer))); else if (path instanceof ArrayBuffer || path instanceof SharedArrayBuffer) buffArr.push(Promise.resolve(new Uint8Array(path))); else if (existsSync(path)) buffArr.push(readFile(path)); else throw new Error(`Unable to concat '${path}': Invalid path.`); const arr = await Promise.all(buffArr); // Concat all buffers into the new ones. const length = arr.reduce((p, c) => p + c.length, 0); const result = new Uint8Array(length); let preBuffLen = 0; for (const buff of arr) result.set(buff, preBuffLen), preBuffLen = buff.length; return result; } /** * Maps Node.js architecture to the AppImage-friendly format. */ export const mapArch = Object.freeze({ x64: "x86_64", ia32: "i686", arm64: "aarch64", armv7l: "armhf" }); //# sourceMappingURL=utils.js.map