@reforged/maker-appimage
Version:
An AppImage maker implementation for the Electron Forge.
134 lines • 5.37 kB
JavaScript
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