forked from olcxjas-softworks/LarpixClient
197 lines
No EOL
10 KiB
JavaScript
197 lines
No EOL
10 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.LibUiFramework = exports.LAUNCHUI_DEFAULT_VERSION = void 0;
|
|
exports.validateShellEmbeddable = validateShellEmbeddable;
|
|
exports.getNodeJsDownloadParams = getNodeJsDownloadParams;
|
|
exports.downloadNodeJsBinary = downloadNodeJsBinary;
|
|
exports.fetchNodeJsChecksum = fetchNodeJsChecksum;
|
|
exports.getLaunchUiDownloadParams = getLaunchUiDownloadParams;
|
|
exports.downloadLaunchUiDir = downloadLaunchUiDir;
|
|
const builder_util_1 = require("builder-util");
|
|
const fs_extra_1 = require("fs-extra");
|
|
const promises_1 = require("fs/promises");
|
|
const https = require("https");
|
|
const path = require("path");
|
|
const core_1 = require("../core");
|
|
const electronGet_1 = require("../util/electronGet");
|
|
const plist_1 = require("../util/plist");
|
|
/** Validates that a value is safe to embed in a double-quoted shell string (no metacharacters). */
|
|
function validateShellEmbeddable(value, fieldName) {
|
|
// Allow letters, digits, dots, underscores, hyphens, forward slashes, and spaces.
|
|
// Reject anything that could be interpreted as a shell metacharacter when embedded
|
|
// inside a double-quoted string: $, `, ", \, and newlines.
|
|
if (/[$`"\\\n]/.test(value)) {
|
|
throw new builder_util_1.InvalidConfigurationError(`${fieldName} contains characters that are not safe in shell scripts: ${JSON.stringify(value)}. ` + `Avoid $, backtick, double-quote, backslash, and newline characters.`);
|
|
}
|
|
}
|
|
// LaunchUI version is independent of the Node.js version; this was the hardcoded default in the Go binary.
|
|
// https://github.com/develar/app-builder/blob/master/pkg/package-format/proton-native/protonNative.go#L105-L136
|
|
exports.LAUNCHUI_DEFAULT_VERSION = "0.1.4-10.13.0";
|
|
// https://github.com/develar/launchui/releases/tag/v0.1.4-10.13.0
|
|
const launchUiChecksums = {
|
|
"launchui-v0.1.4-10.13.0-linux-x64.7z": "4fb5cd8ed79e1e24e0f5cf4b26107f2fa6f6fd8dc48ecd18fb6f48f3ccfe9ee6",
|
|
"launchui-v0.1.4-10.13.0-win32-ia32.7z": "682734da3d817ac365093c6c8ef3d9a70cc3f2a809e4588cb12a311358a68a2d",
|
|
"launchui-v0.1.4-10.13.0-win32-x64.7z": "2f26629c5f5c12baeff272ac7855a1df7f27621cce782b79965f9a9b5eccc359",
|
|
};
|
|
class LibUiFramework {
|
|
constructor(version, macOsProductName, isUseLaunchUi) {
|
|
this.version = version;
|
|
this.macOsProductName = macOsProductName;
|
|
this.isUseLaunchUi = isUseLaunchUi;
|
|
this.name = "libui";
|
|
// noinspection JSUnusedGlobalSymbols
|
|
this.macOsDefaultTargets = ["dmg"];
|
|
this.defaultAppIdPrefix = "com.libui.";
|
|
// noinspection JSUnusedGlobalSymbols
|
|
this.isCopyElevateHelper = false;
|
|
// noinspection JSUnusedGlobalSymbols
|
|
this.isNpmRebuildRequired = false;
|
|
this.launchUiVersion = exports.LAUNCHUI_DEFAULT_VERSION;
|
|
}
|
|
get distMacOsAppName() {
|
|
return `${this.macOsProductName}.app`;
|
|
}
|
|
async prepareApplicationStageDirectory(options) {
|
|
await (0, fs_extra_1.emptyDir)(options.appOutDir);
|
|
const packager = options.packager;
|
|
const platform = packager.platform;
|
|
if (this.isUseLaunchUiForPlatform(platform)) {
|
|
const appOutDir = options.appOutDir;
|
|
const launchUiDir = await downloadLaunchUiDir(this.launchUiVersion, platform, options.arch);
|
|
await (0, fs_extra_1.copy)(launchUiDir, appOutDir);
|
|
const skeletonExe = `launchui${platform === core_1.Platform.WINDOWS ? ".exe" : ""}`;
|
|
const executableName = `${packager.appInfo.productFilename}${platform === core_1.Platform.WINDOWS ? ".exe" : ""}`;
|
|
await (0, promises_1.rename)(path.join(appOutDir, skeletonExe), path.join(appOutDir, executableName));
|
|
return;
|
|
}
|
|
if (platform === core_1.Platform.MAC) {
|
|
await this.prepareMacosApplicationStageDirectory(packager, options);
|
|
}
|
|
else if (platform === core_1.Platform.LINUX) {
|
|
await this.prepareLinuxApplicationStageDirectory(options);
|
|
}
|
|
}
|
|
async prepareMacosApplicationStageDirectory(packager, options) {
|
|
const appContentsDir = path.join(options.appOutDir, this.distMacOsAppName, "Contents");
|
|
await (0, promises_1.mkdir)(path.join(appContentsDir, "Resources"), { recursive: true });
|
|
await (0, promises_1.mkdir)(path.join(appContentsDir, "MacOS"), { recursive: true });
|
|
const nodeBinaryMac = await downloadNodeJsBinary(this.version, core_1.Platform.MAC, "x64");
|
|
await (0, promises_1.copyFile)(nodeBinaryMac, path.join(appContentsDir, "MacOS", "node"));
|
|
await (0, promises_1.chmod)(path.join(appContentsDir, "MacOS", "node"), 0o755);
|
|
const appPlist = {
|
|
// https://github.com/albe-rosado/create-proton-app/issues/13
|
|
NSHighResolutionCapable: true,
|
|
};
|
|
await packager.applyCommonInfo(appPlist, appContentsDir);
|
|
await (0, plist_1.savePlistFile)(path.join(appContentsDir, "Info.plist"), appPlist);
|
|
const macMain = options.packager.info.metadata.main || "index.js";
|
|
validateShellEmbeddable(macMain, "package.json main");
|
|
await writeExecutableMain(path.join(appContentsDir, "MacOS", appPlist.CFBundleExecutable), `#!/bin/sh
|
|
DIR=$(dirname "$0")
|
|
"$DIR/node" "$DIR/../Resources/app/${macMain}"
|
|
`);
|
|
}
|
|
async prepareLinuxApplicationStageDirectory(options) {
|
|
const appOutDir = options.appOutDir;
|
|
const nodeBinaryLinux = await downloadNodeJsBinary(this.version, core_1.Platform.LINUX, options.arch);
|
|
await (0, promises_1.copyFile)(nodeBinaryLinux, path.join(appOutDir, "node"));
|
|
await (0, promises_1.chmod)(path.join(appOutDir, "node"), 0o755);
|
|
const mainPath = path.join(appOutDir, options.packager.executableName);
|
|
const linuxMain = options.packager.info.metadata.main || "index.js";
|
|
validateShellEmbeddable(linuxMain, "package.json main");
|
|
await writeExecutableMain(mainPath, `#!/bin/sh
|
|
DIR=$(dirname "$0")
|
|
"$DIR/node" "$DIR/app/${linuxMain}"
|
|
`);
|
|
}
|
|
async afterPack(context) {
|
|
const packager = context.packager;
|
|
if (!this.isUseLaunchUiForPlatform(packager.platform)) {
|
|
return;
|
|
}
|
|
// LaunchUI requires main.js, rename if need
|
|
const userMain = packager.info.metadata.main || "index.js";
|
|
if (userMain === "main.js") {
|
|
return;
|
|
}
|
|
await (0, promises_1.rename)(path.join(context.appOutDir, "app", userMain), path.join(context.appOutDir, "app", "main.js"));
|
|
}
|
|
getMainFile(platform) {
|
|
return this.isUseLaunchUiForPlatform(platform) ? "main.js" : null;
|
|
}
|
|
isUseLaunchUiForPlatform(platform) {
|
|
return platform === core_1.Platform.WINDOWS || (this.isUseLaunchUi && platform === core_1.Platform.LINUX);
|
|
}
|
|
getExcludedDependencies(platform) {
|
|
// part of launchui
|
|
return this.isUseLaunchUiForPlatform(platform) ? ["libui-node"] : null;
|
|
}
|
|
}
|
|
exports.LibUiFramework = LibUiFramework;
|
|
async function writeExecutableMain(file, content) {
|
|
await (0, promises_1.writeFile)(file, content, { mode: 0o755 });
|
|
await (0, promises_1.chmod)(file, 0o755);
|
|
}
|
|
function getNodeJsDownloadParams(version, platform, arch) {
|
|
const isWindows = platform === core_1.Platform.WINDOWS;
|
|
const nodePlatform = isWindows ? "win" : platform === core_1.Platform.MAC ? "darwin" : "linux";
|
|
const nodeArch = isWindows && arch === "ia32" ? "x86" : arch;
|
|
const format = isWindows ? "zip" : "tar.gz";
|
|
const filenameWithExt = `node-v${version}-${nodePlatform}-${nodeArch}.${format}`;
|
|
// tar.gz: strip:1 moves node-v.../bin/node → bin/node in extractDir
|
|
// zip: no strip, node.exe lives under the top-level dir node-v{version}-win-{arch}/
|
|
const binaryRelPath = isWindows ? path.join(`node-v${version}-win-${nodeArch}`, "node.exe") : path.join("bin", "node");
|
|
return { releaseName: `nodejs-v${version}`, filenameWithExt, overrideUrl: `https://nodejs.org/dist/v${version}`, binaryRelPath };
|
|
}
|
|
async function downloadNodeJsBinary(version, platform, arch) {
|
|
const { releaseName, filenameWithExt, overrideUrl, binaryRelPath } = getNodeJsDownloadParams(version, platform, arch);
|
|
const sha256 = await fetchNodeJsChecksum(version, filenameWithExt);
|
|
const checksums = { [filenameWithExt]: sha256 };
|
|
const extractDir = await (0, electronGet_1.downloadBuilderToolset)({ releaseName, filenameWithExt, overrideUrl, checksums });
|
|
return path.join(extractDir, binaryRelPath);
|
|
}
|
|
/**
|
|
* Fetches the SHA-256 hex digest for a specific Node.js distribution file from
|
|
* the official nodejs.org SHASUMS256.txt, preventing MITM substitution attacks.
|
|
*/
|
|
async function fetchNodeJsChecksum(version, filename) {
|
|
const url = `https://nodejs.org/dist/v${version}/SHASUMS256.txt`;
|
|
return new Promise((resolve, reject) => {
|
|
https
|
|
.get(url, { headers: { "User-Agent": "electron-builder" } }, res => {
|
|
if (res.statusCode !== 200) {
|
|
res.resume();
|
|
reject(new Error(`HTTP ${res.statusCode} fetching Node.js SHASUMS256.txt for v${version}`));
|
|
return;
|
|
}
|
|
const chunks = [];
|
|
res.on("data", (c) => chunks.push(c));
|
|
res.on("end", () => {
|
|
const text = Buffer.concat(chunks).toString("utf8");
|
|
for (const line of text.split("\n")) {
|
|
const m = line.match(/^([0-9a-f]{64})\s+(.+)$/);
|
|
if (m != null && m[2].trim() === filename) {
|
|
resolve(m[1]);
|
|
return;
|
|
}
|
|
}
|
|
reject(new Error(`No checksum for ${filename} in Node.js v${version} SHASUMS256.txt`));
|
|
});
|
|
res.on("error", reject);
|
|
})
|
|
.on("error", reject);
|
|
});
|
|
}
|
|
function getLaunchUiDownloadParams(version, platform, arch) {
|
|
const launchPlatform = platform === core_1.Platform.MAC ? "mac" : platform === core_1.Platform.WINDOWS ? "win32" : "linux";
|
|
return {
|
|
releaseName: `v${version}`,
|
|
filenameWithExt: `launchui-v${version}-${launchPlatform}-${arch}.7z`,
|
|
githubOrgRepo: "develar/launchui",
|
|
checksums: launchUiChecksums,
|
|
};
|
|
}
|
|
async function downloadLaunchUiDir(version, platform, arch) {
|
|
return (0, electronGet_1.downloadBuilderToolset)(getLaunchUiDownloadParams(version, platform, arch));
|
|
}
|
|
//# sourceMappingURL=LibUiFramework.js.map
|