This commit is contained in:
root
2025-11-25 09:56:15 +03:00
commit 68c8f0e80d
23717 changed files with 3200521 additions and 0 deletions

View File

@ -0,0 +1,14 @@
import { Arch } from "builder-util";
import { Target } from "../core";
import { LinuxPackager } from "../linuxPackager";
import { AppImageOptions } from "../options/linuxOptions";
import { LinuxTargetHelper } from "./LinuxTargetHelper";
export default class AppImageTarget extends Target {
private readonly packager;
private readonly helper;
readonly outDir: string;
readonly options: AppImageOptions;
private readonly desktopEntry;
constructor(ignored: string, packager: LinuxPackager, helper: LinuxTargetHelper, outDir: string);
build(appOutDir: string, arch: Arch): Promise<any>;
}

View File

@ -0,0 +1,97 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const builder_util_1 = require("builder-util");
const fs_extra_1 = require("fs-extra");
const lazy_val_1 = require("lazy-val");
const path = require("path");
const core_1 = require("../core");
const PublishManager_1 = require("../publish/PublishManager");
const appBuilder_1 = require("../util/appBuilder");
const license_1 = require("../util/license");
const targetUtil_1 = require("./targetUtil");
// https://unix.stackexchange.com/questions/375191/append-to-sub-directory-inside-squashfs-file
class AppImageTarget extends core_1.Target {
constructor(ignored, packager, helper, outDir) {
super("appImage");
this.packager = packager;
this.helper = helper;
this.outDir = outDir;
this.options = { ...this.packager.platformSpecificBuildOptions, ...this.packager.config[this.name] };
this.desktopEntry = new lazy_val_1.Lazy(() => {
var _a;
const args = ((_a = this.options.executableArgs) === null || _a === void 0 ? void 0 : _a.join(" ")) || "--no-sandbox";
return helper.computeDesktopEntry(this.options, `AppRun ${args} %U`, {
"X-AppImage-Version": `${packager.appInfo.buildVersion}`,
});
});
}
async build(appOutDir, arch) {
const packager = this.packager;
const options = this.options;
// https://github.com/electron-userland/electron-builder/issues/775
// https://github.com/electron-userland/electron-builder/issues/1726
// tslint:disable-next-line:no-invalid-template-strings
const artifactName = packager.expandArtifactNamePattern(options, "AppImage", arch);
const artifactPath = path.join(this.outDir, artifactName);
await packager.info.callArtifactBuildStarted({
targetPresentableName: "AppImage",
file: artifactPath,
arch,
});
const c = await Promise.all([
this.desktopEntry.value,
this.helper.icons,
(0, PublishManager_1.getAppUpdatePublishConfiguration)(packager, arch, false /* in any case validation will be done on publish */),
(0, license_1.getNotLocalizedLicenseFile)(options.license, this.packager, ["txt", "html"]),
(0, targetUtil_1.createStageDir)(this, packager, arch),
]);
const license = c[3];
const stageDir = c[4];
const publishConfig = c[2];
if (publishConfig != null) {
await (0, fs_extra_1.outputFile)(path.join(packager.getResourcesDir(stageDir.dir), "app-update.yml"), (0, builder_util_1.serializeToYaml)(publishConfig));
}
if (this.packager.packagerOptions.effectiveOptionComputed != null &&
(await this.packager.packagerOptions.effectiveOptionComputed({ desktop: await this.desktopEntry.value }))) {
return;
}
const args = [
"appimage",
"--stage",
stageDir.dir,
"--arch",
builder_util_1.Arch[arch],
"--output",
artifactPath,
"--app",
appOutDir,
"--configuration",
JSON.stringify({
productName: this.packager.appInfo.productName,
productFilename: this.packager.appInfo.productFilename,
desktopEntry: c[0],
executableName: this.packager.executableName,
icons: c[1],
fileAssociations: this.packager.fileAssociations,
...options,
}),
];
(0, appBuilder_1.objectToArgs)(args, {
license,
});
if (packager.compression === "maximum") {
args.push("--compression", "xz");
}
await packager.info.callArtifactBuildCompleted({
file: artifactPath,
safeArtifactName: packager.computeSafeArtifactName(artifactName, "AppImage", arch, false),
target: this,
arch,
packager,
isWriteUpdateInfo: true,
updateInfo: await (0, appBuilder_1.executeAppBuilderAsJson)(args),
});
}
}
exports.default = AppImageTarget;
//# sourceMappingURL=AppImageTarget.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,15 @@
import { Arch } from "builder-util";
import { AppXOptions } from "../";
import { Target } from "../core";
import { WinPackager } from "../winPackager";
export default class AppXTarget extends Target {
private readonly packager;
readonly outDir: string;
readonly options: AppXOptions;
constructor(packager: WinPackager, outDir: string);
build(appOutDir: string, arch: Arch): Promise<any>;
private static computeUserAssets;
private computePublisherName;
private writeManifest;
private getExtensions;
}

View File

@ -0,0 +1,315 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const bluebird_lst_1 = require("bluebird-lst");
const builder_util_1 = require("builder-util");
const fs_1 = require("builder-util/out/fs");
const fs_extra_1 = require("fs-extra");
const path = require("path");
const windowsCodeSign_1 = require("../codeSign/windowsCodeSign");
const core_1 = require("../core");
const pathManager_1 = require("../util/pathManager");
const targetUtil_1 = require("./targetUtil");
const APPX_ASSETS_DIR_NAME = "appx";
const vendorAssetsForDefaultAssets = {
"StoreLogo.png": "SampleAppx.50x50.png",
"Square150x150Logo.png": "SampleAppx.150x150.png",
"Square44x44Logo.png": "SampleAppx.44x44.png",
"Wide310x150Logo.png": "SampleAppx.310x150.png",
};
const DEFAULT_RESOURCE_LANG = "en-US";
class AppXTarget extends core_1.Target {
constructor(packager, outDir) {
super("appx");
this.packager = packager;
this.outDir = outDir;
this.options = (0, builder_util_1.deepAssign)({}, this.packager.platformSpecificBuildOptions, this.packager.config.appx);
if (process.platform !== "darwin" && (process.platform !== "win32" || (0, windowsCodeSign_1.isOldWin6)())) {
throw new Error("AppX is supported only on Windows 10 or Windows Server 2012 R2 (version number 6.3+)");
}
}
// https://docs.microsoft.com/en-us/windows/uwp/packaging/create-app-package-with-makeappx-tool#mapping-files
async build(appOutDir, arch) {
const packager = this.packager;
const artifactName = packager.expandArtifactBeautyNamePattern(this.options, "appx", arch);
const artifactPath = path.join(this.outDir, artifactName);
await packager.info.callArtifactBuildStarted({
targetPresentableName: "AppX",
file: artifactPath,
arch,
});
const vendorPath = await (0, windowsCodeSign_1.getSignVendorPath)();
const vm = await packager.vm.value;
const stageDir = await (0, targetUtil_1.createStageDir)(this, packager, arch);
const mappingFile = stageDir.getTempFile("mapping.txt");
const makeAppXArgs = ["pack", "/o" /* overwrite the output file if it exists */, "/f", vm.toVmFile(mappingFile), "/p", vm.toVmFile(artifactPath)];
if (packager.compression === "store") {
makeAppXArgs.push("/nc");
}
const mappingList = [];
mappingList.push(await bluebird_lst_1.default.map((0, fs_1.walk)(appOutDir), file => {
let appxPath = file.substring(appOutDir.length + 1);
if (path.sep !== "\\") {
appxPath = appxPath.replace(/\//g, "\\");
}
return `"${vm.toVmFile(file)}" "app\\${appxPath}"`;
}));
const userAssetDir = await this.packager.getResource(undefined, APPX_ASSETS_DIR_NAME);
const assetInfo = await AppXTarget.computeUserAssets(vm, vendorPath, userAssetDir);
const userAssets = assetInfo.userAssets;
const manifestFile = stageDir.getTempFile("AppxManifest.xml");
await this.writeManifest(manifestFile, arch, await this.computePublisherName(), userAssets);
await packager.info.callAppxManifestCreated(manifestFile);
mappingList.push(assetInfo.mappings);
mappingList.push([`"${vm.toVmFile(manifestFile)}" "AppxManifest.xml"`]);
const signToolArch = arch === builder_util_1.Arch.arm64 ? "x64" : builder_util_1.Arch[arch];
if (isScaledAssetsProvided(userAssets)) {
const outFile = vm.toVmFile(stageDir.getTempFile("resources.pri"));
const makePriPath = vm.toVmFile(path.join(vendorPath, "windows-10", signToolArch, "makepri.exe"));
const assetRoot = stageDir.getTempFile("appx/assets");
await (0, fs_extra_1.emptyDir)(assetRoot);
await bluebird_lst_1.default.map(assetInfo.allAssets, it => (0, fs_1.copyOrLinkFile)(it, path.join(assetRoot, path.basename(it))));
await vm.exec(makePriPath, [
"new",
"/Overwrite",
"/Manifest",
vm.toVmFile(manifestFile),
"/ProjectRoot",
vm.toVmFile(path.dirname(assetRoot)),
"/ConfigXml",
vm.toVmFile(path.join((0, pathManager_1.getTemplatePath)("appx"), "priconfig.xml")),
"/OutputFile",
outFile,
]);
// in addition to resources.pri, resources.scale-140.pri and other such files will be generated
for (const resourceFile of (await (0, fs_extra_1.readdir)(stageDir.dir)).filter(it => it.startsWith("resources.")).sort()) {
mappingList.push([`"${vm.toVmFile(stageDir.getTempFile(resourceFile))}" "${resourceFile}"`]);
}
makeAppXArgs.push("/l");
}
let mapping = "[Files]";
for (const list of mappingList) {
mapping += "\r\n" + list.join("\r\n");
}
await (0, fs_extra_1.writeFile)(mappingFile, mapping);
packager.debugLogger.add("appx.mapping", mapping);
if (this.options.makeappxArgs != null) {
makeAppXArgs.push(...this.options.makeappxArgs);
}
await vm.exec(vm.toVmFile(path.join(vendorPath, "windows-10", signToolArch, "makeappx.exe")), makeAppXArgs);
await packager.sign(artifactPath);
await stageDir.cleanup();
await packager.info.callArtifactBuildCompleted({
file: artifactPath,
packager,
arch,
safeArtifactName: packager.computeSafeArtifactName(artifactName, "appx"),
target: this,
isWriteUpdateInfo: this.options.electronUpdaterAware,
});
}
static async computeUserAssets(vm, vendorPath, userAssetDir) {
const mappings = [];
let userAssets;
const allAssets = [];
if (userAssetDir == null) {
userAssets = [];
}
else {
userAssets = (await (0, fs_extra_1.readdir)(userAssetDir)).filter(it => !it.startsWith(".") && !it.endsWith(".db") && it.includes("."));
for (const name of userAssets) {
mappings.push(`"${vm.toVmFile(userAssetDir)}${vm.pathSep}${name}" "assets\\${name}"`);
allAssets.push(path.join(userAssetDir, name));
}
}
for (const defaultAsset of Object.keys(vendorAssetsForDefaultAssets)) {
if (userAssets.length === 0 || !isDefaultAssetIncluded(userAssets, defaultAsset)) {
const file = path.join(vendorPath, "appxAssets", vendorAssetsForDefaultAssets[defaultAsset]);
mappings.push(`"${vm.toVmFile(file)}" "assets\\${defaultAsset}"`);
allAssets.push(file);
}
}
// we do not use process.arch to build path to tools, because even if you are on x64, ia32 appx tool must be used if you build appx for ia32
return { userAssets, mappings, allAssets };
}
// https://github.com/electron-userland/electron-builder/issues/2108#issuecomment-333200711
async computePublisherName() {
if ((await this.packager.cscInfo.value) == null) {
builder_util_1.log.info({ reason: "Windows Store only build" }, "AppX is not signed");
return this.options.publisher || "CN=ms";
}
const certInfo = await this.packager.lazyCertInfo.value;
const publisher = this.options.publisher || (certInfo == null ? null : certInfo.bloodyMicrosoftSubjectDn);
if (publisher == null) {
throw new Error("Internal error: cannot compute subject using certificate info");
}
return publisher;
}
async writeManifest(outFile, arch, publisher, userAssets) {
const appInfo = this.packager.appInfo;
const options = this.options;
const executable = `app\\${appInfo.productFilename}.exe`;
const displayName = options.displayName || appInfo.productName;
const extensions = await this.getExtensions(executable, displayName);
const manifest = (await (0, fs_extra_1.readFile)(path.join((0, pathManager_1.getTemplatePath)("appx"), "appxmanifest.xml"), "utf8")).replace(/\${([a-zA-Z0-9]+)}/g, (match, p1) => {
switch (p1) {
case "publisher":
return publisher;
case "publisherDisplayName": {
const name = options.publisherDisplayName || appInfo.companyName;
if (name == null) {
throw new builder_util_1.InvalidConfigurationError(`Please specify "author" in the application package.json — it is required because "appx.publisherDisplayName" is not set.`);
}
return name;
}
case "version":
return appInfo.getVersionInWeirdWindowsForm(options.setBuildNumber === true);
case "applicationId": {
const result = options.applicationId || options.identityName || appInfo.name;
if (!isNaN(parseInt(result[0], 10))) {
let message = `AppX Application.Id cant start with numbers: "${result}"`;
if (options.applicationId == null) {
message += `\nPlease set appx.applicationId (or correct appx.identityName or name)`;
}
throw new builder_util_1.InvalidConfigurationError(message);
}
return result;
}
case "identityName":
return options.identityName || appInfo.name;
case "executable":
return executable;
case "displayName":
return displayName;
case "description":
return appInfo.description || appInfo.productName;
case "backgroundColor":
return options.backgroundColor || "#464646";
case "logo":
return "assets\\StoreLogo.png";
case "square150x150Logo":
return "assets\\Square150x150Logo.png";
case "square44x44Logo":
return "assets\\Square44x44Logo.png";
case "lockScreen":
return lockScreenTag(userAssets);
case "defaultTile":
return defaultTileTag(userAssets, options.showNameOnTiles || false);
case "splashScreen":
return splashScreenTag(userAssets);
case "arch":
return arch === builder_util_1.Arch.ia32 ? "x86" : arch === builder_util_1.Arch.arm64 ? "arm64" : "x64";
case "resourceLanguages":
return resourceLanguageTag((0, builder_util_1.asArray)(options.languages));
case "extensions":
return extensions;
case "minVersion":
return arch === builder_util_1.Arch.arm64 ? "10.0.16299.0" : "10.0.14316.0";
case "maxVersionTested":
return arch === builder_util_1.Arch.arm64 ? "10.0.16299.0" : "10.0.14316.0";
default:
throw new Error(`Macro ${p1} is not defined`);
}
});
await (0, fs_extra_1.writeFile)(outFile, manifest);
}
async getExtensions(executable, displayName) {
const uriSchemes = (0, builder_util_1.asArray)(this.packager.config.protocols).concat((0, builder_util_1.asArray)(this.packager.platformSpecificBuildOptions.protocols));
const fileAssociations = (0, builder_util_1.asArray)(this.packager.config.fileAssociations).concat((0, builder_util_1.asArray)(this.packager.platformSpecificBuildOptions.fileAssociations));
let isAddAutoLaunchExtension = this.options.addAutoLaunchExtension;
if (isAddAutoLaunchExtension === undefined) {
const deps = this.packager.info.metadata.dependencies;
isAddAutoLaunchExtension = deps != null && deps["electron-winstore-auto-launch"] != null;
}
if (!isAddAutoLaunchExtension && uriSchemes.length === 0 && fileAssociations.length === 0 && this.options.customExtensionsPath === undefined) {
return "";
}
let extensions = "<Extensions>";
if (isAddAutoLaunchExtension) {
extensions += `
<desktop:Extension Category="windows.startupTask" Executable="${executable}" EntryPoint="Windows.FullTrustApplication">
<desktop:StartupTask TaskId="SlackStartup" Enabled="true" DisplayName="${displayName}" />
</desktop:Extension>`;
}
for (const protocol of uriSchemes) {
for (const scheme of (0, builder_util_1.asArray)(protocol.schemes)) {
extensions += `
<uap:Extension Category="windows.protocol">
<uap:Protocol Name="${scheme}">
<uap:DisplayName>${protocol.name}</uap:DisplayName>
</uap:Protocol>
</uap:Extension>`;
}
}
for (const fileAssociation of fileAssociations) {
for (const ext of (0, builder_util_1.asArray)(fileAssociation.ext)) {
extensions += `
<uap:Extension Category="windows.fileTypeAssociation">
<uap:FileTypeAssociation Name="${ext}">
<uap:SupportedFileTypes>
<uap:FileType>.${ext}</uap:FileType>
</uap:SupportedFileTypes>
</uap:FileTypeAssociation>
</uap:Extension>`;
}
}
if (this.options.customExtensionsPath !== undefined) {
const extensionsPath = path.resolve(this.packager.info.appDir, this.options.customExtensionsPath);
extensions += await (0, fs_extra_1.readFile)(extensionsPath, "utf8");
}
extensions += "</Extensions>";
return extensions;
}
}
exports.default = AppXTarget;
// get the resource - language tag, see https://docs.microsoft.com/en-us/windows/uwp/globalizing/manage-language-and-region#specify-the-supported-languages-in-the-apps-manifest
function resourceLanguageTag(userLanguages) {
if (userLanguages == null || userLanguages.length === 0) {
userLanguages = [DEFAULT_RESOURCE_LANG];
}
return userLanguages.map(it => `<Resource Language="${it.replace(/_/g, "-")}" />`).join("\n");
}
function lockScreenTag(userAssets) {
if (isDefaultAssetIncluded(userAssets, "BadgeLogo.png")) {
return '<uap:LockScreen Notification="badgeAndTileText" BadgeLogo="assets\\BadgeLogo.png" />';
}
else {
return "";
}
}
function defaultTileTag(userAssets, showNameOnTiles) {
const defaultTiles = ["<uap:DefaultTile", 'Wide310x150Logo="assets\\Wide310x150Logo.png"'];
if (isDefaultAssetIncluded(userAssets, "LargeTile.png")) {
defaultTiles.push('Square310x310Logo="assets\\LargeTile.png"');
}
if (isDefaultAssetIncluded(userAssets, "SmallTile.png")) {
defaultTiles.push('Square71x71Logo="assets\\SmallTile.png"');
}
if (showNameOnTiles) {
defaultTiles.push(">");
defaultTiles.push("<uap:ShowNameOnTiles>");
defaultTiles.push("<uap:ShowOn", 'Tile="wide310x150Logo"', "/>");
defaultTiles.push("<uap:ShowOn", 'Tile="square150x150Logo"', "/>");
defaultTiles.push("</uap:ShowNameOnTiles>");
defaultTiles.push("</uap:DefaultTile>");
}
else {
defaultTiles.push("/>");
}
return defaultTiles.join(" ");
}
function splashScreenTag(userAssets) {
if (isDefaultAssetIncluded(userAssets, "SplashScreen.png")) {
return '<uap:SplashScreen Image="assets\\SplashScreen.png" />';
}
else {
return "";
}
}
function isDefaultAssetIncluded(userAssets, defaultAsset) {
const defaultAssetName = defaultAsset.substring(0, defaultAsset.indexOf("."));
return userAssets.some(it => it.includes(defaultAssetName));
}
function isScaledAssetsProvided(userAssets) {
return userAssets.some(it => it.includes(".scale-") || it.includes(".targetsize-"));
}
//# sourceMappingURL=AppxTarget.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,11 @@
import { Arch } from "builder-util";
import { Target, TargetSpecificOptions } from "../core";
import { PlatformPackager } from "../platformPackager";
export declare class ArchiveTarget extends Target {
readonly outDir: string;
private readonly packager;
private readonly isWriteUpdateInfo;
readonly options: TargetSpecificOptions;
constructor(name: string, outDir: string, packager: PlatformPackager<any>, isWriteUpdateInfo?: boolean);
build(appOutDir: string, arch: Arch): Promise<any>;
}

View File

@ -0,0 +1,84 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ArchiveTarget = void 0;
const builder_util_1 = require("builder-util");
const path = require("path");
const core_1 = require("../core");
const fileMatcher_1 = require("../fileMatcher");
const archive_1 = require("./archive");
const differentialUpdateInfoBuilder_1 = require("./differentialUpdateInfoBuilder");
class ArchiveTarget extends core_1.Target {
constructor(name, outDir, packager, isWriteUpdateInfo = false) {
super(name);
this.outDir = outDir;
this.packager = packager;
this.isWriteUpdateInfo = isWriteUpdateInfo;
this.options = this.packager.config[this.name];
}
async build(appOutDir, arch) {
const packager = this.packager;
const isMac = packager.platform === core_1.Platform.MAC;
const format = this.name;
let defaultPattern;
const defaultArch = (0, builder_util_1.defaultArchFromString)(packager.platformSpecificBuildOptions.defaultArch);
if (packager.platform === core_1.Platform.LINUX) {
// tslint:disable-next-line:no-invalid-template-strings
defaultPattern = "${name}-${version}" + (arch === defaultArch ? "" : "-${arch}") + ".${ext}";
}
else {
// tslint:disable-next-line:no-invalid-template-strings
defaultPattern = "${productName}-${version}" + (arch === defaultArch ? "" : "-${arch}") + "-${os}.${ext}";
}
const artifactName = packager.expandArtifactNamePattern(this.options, format, arch, defaultPattern, false);
const artifactPath = path.join(this.outDir, artifactName);
await packager.info.callArtifactBuildStarted({
targetPresentableName: `${isMac ? "macOS " : ""}${format}`,
file: artifactPath,
arch,
});
let updateInfo = null;
if (format.startsWith("tar.")) {
await (0, archive_1.tar)(packager.compression, format, artifactPath, appOutDir, isMac, packager.info.tempDirManager);
}
else {
let withoutDir = !isMac;
let dirToArchive = appOutDir;
if (isMac) {
dirToArchive = path.dirname(appOutDir);
const fileMatchers = (0, fileMatcher_1.getFileMatchers)(packager.config, "extraDistFiles", dirToArchive, packager.createGetFileMatchersOptions(this.outDir, arch, packager.platformSpecificBuildOptions));
if (fileMatchers == null) {
dirToArchive = appOutDir;
}
else {
await (0, fileMatcher_1.copyFiles)(fileMatchers, null, true);
withoutDir = true;
}
}
const archiveOptions = {
compression: packager.compression,
withoutDir,
};
await (0, archive_1.archive)(format, artifactPath, dirToArchive, archiveOptions);
if (this.isWriteUpdateInfo && format === "zip") {
if (isMac) {
updateInfo = await (0, differentialUpdateInfoBuilder_1.createBlockmap)(artifactPath, this, packager, artifactName);
}
else {
updateInfo = await (0, differentialUpdateInfoBuilder_1.appendBlockmap)(artifactPath);
}
}
}
await packager.info.callArtifactBuildCompleted({
updateInfo,
file: artifactPath,
// tslint:disable-next-line:no-invalid-template-strings
safeArtifactName: packager.computeSafeArtifactName(artifactName, format, arch, false, packager.platformSpecificBuildOptions.defaultArch, defaultPattern.replace("${productName}", "${name}")),
target: this,
arch,
packager,
isWriteUpdateInfo: this.isWriteUpdateInfo,
});
}
}
exports.ArchiveTarget = ArchiveTarget;
//# sourceMappingURL=ArchiveTarget.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,20 @@
import { Arch } from "builder-util";
import { Target } from "../core";
import { LinuxPackager } from "../linuxPackager";
import { FlatpakOptions } from "../options/linuxOptions";
import { LinuxTargetHelper } from "./LinuxTargetHelper";
export default class FlatpakTarget extends Target {
private readonly packager;
private helper;
readonly outDir: string;
readonly options: FlatpakOptions;
constructor(name: string, packager: LinuxPackager, helper: LinuxTargetHelper, outDir: string);
get appId(): string;
build(appOutDir: string, arch: Arch): Promise<any>;
private prepareStageDir;
private createSandboxBinWrapper;
private createDesktopFile;
private copyLicenseFile;
private copyIcons;
private getFlatpakBuilderOptions;
}

View File

@ -0,0 +1,159 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const flatpak_bundler_1 = require("@malept/flatpak-bundler");
const builder_util_1 = require("builder-util");
const fs_extra_1 = require("fs-extra");
const path = require("path");
const core_1 = require("../core");
const license_1 = require("../util/license");
const targetUtil_1 = require("./targetUtil");
class FlatpakTarget extends core_1.Target {
constructor(name, packager, helper, outDir) {
super(name);
this.packager = packager;
this.helper = helper;
this.outDir = outDir;
this.options = {
...this.packager.platformSpecificBuildOptions,
...this.packager.config[this.name],
};
}
get appId() {
return filterFlatpakAppIdentifier(this.packager.appInfo.id);
}
async build(appOutDir, arch) {
const { packager, options } = this;
const artifactName = packager.expandArtifactNamePattern(options, "flatpak", arch, undefined, false);
const artifactPath = path.join(this.outDir, artifactName);
await packager.info.callArtifactBuildStarted({
targetPresentableName: "flatpak",
file: artifactPath,
arch,
});
const stageDir = await this.prepareStageDir(arch);
const { manifest, buildOptions } = this.getFlatpakBuilderOptions(appOutDir, stageDir.dir, artifactName, arch);
await (0, flatpak_bundler_1.bundle)(manifest, buildOptions);
await stageDir.cleanup();
await packager.info.callArtifactBuildCompleted({
file: artifactPath,
safeArtifactName: packager.computeSafeArtifactName(artifactName, "flatpak", arch, false),
target: this,
arch,
packager,
isWriteUpdateInfo: false,
});
}
async prepareStageDir(arch) {
const stageDir = await (0, targetUtil_1.createStageDir)(this, this.packager, arch);
await Promise.all([this.createSandboxBinWrapper(stageDir), this.createDesktopFile(stageDir), this.copyLicenseFile(stageDir), this.copyIcons(stageDir)]);
return stageDir;
}
async createSandboxBinWrapper(stageDir) {
const useWaylandFlags = !!this.options.useWaylandFlags;
const electronWrapperPath = stageDir.getTempFile(path.join("bin", "electron-wrapper"));
await (0, fs_extra_1.outputFile)(electronWrapperPath, getElectronWrapperScript(this.packager.executableName, useWaylandFlags));
await (0, fs_extra_1.chmod)(electronWrapperPath, 0o755);
}
async createDesktopFile(stageDir) {
const appIdentifier = this.appId;
const desktopFile = stageDir.getTempFile(path.join("share", "applications", `${appIdentifier}.desktop`));
await this.helper.writeDesktopEntry(this.options, "electron-wrapper %U", desktopFile, { Icon: appIdentifier });
}
async copyLicenseFile(stageDir) {
const licenseSrc = await (0, license_1.getNotLocalizedLicenseFile)(this.options.license, this.packager, ["txt", "html"]);
if (licenseSrc) {
const licenseDst = stageDir.getTempFile(path.join("share", "doc", this.appId, "copyright"));
await (0, builder_util_1.copyFile)(licenseSrc, licenseDst);
}
}
async copyIcons(stageDir) {
const icons = await this.helper.icons;
const copyIcons = icons.map(async (icon) => {
if (icon.size > 512) {
// Flatpak does not allow icons larger than 512 pixels
return Promise.resolve();
}
const extWithDot = path.extname(icon.file);
const sizeName = extWithDot === ".svg" ? "scalable" : `${icon.size}x${icon.size}`;
const iconDst = stageDir.getTempFile(path.join("share", "icons", "hicolor", sizeName, "apps", `${this.appId}${extWithDot}`));
return (0, builder_util_1.copyFile)(icon.file, iconDst);
});
await Promise.all(copyIcons);
}
getFlatpakBuilderOptions(appOutDir, stageDir, artifactName, arch) {
const appIdentifier = this.appId;
const { executableName } = this.packager;
const flatpakArch = (0, builder_util_1.toLinuxArchString)(arch, "flatpak");
const manifest = {
id: appIdentifier,
command: "electron-wrapper",
runtime: this.options.runtime || flatpakBuilderDefaults.runtime,
runtimeVersion: this.options.runtimeVersion || flatpakBuilderDefaults.runtimeVersion,
sdk: this.options.sdk || flatpakBuilderDefaults.sdk,
base: this.options.base || flatpakBuilderDefaults.base,
baseVersion: this.options.baseVersion || flatpakBuilderDefaults.baseVersion,
finishArgs: this.options.finishArgs || flatpakBuilderDefaults.finishArgs,
branch: this.options.branch,
modules: this.options.modules,
};
const buildOptions = {
baseFlatpakref: `app/${manifest.base}/${flatpakArch}/${manifest.baseVersion}`,
runtimeFlatpakref: `runtime/${manifest.runtime}/${flatpakArch}/${manifest.runtimeVersion}`,
sdkFlatpakref: `runtime/${manifest.sdk}/${flatpakArch}/${manifest.runtimeVersion}`,
arch: flatpakArch,
bundlePath: path.join(this.outDir, artifactName),
files: [[stageDir, "/"], [appOutDir, path.join("/lib", appIdentifier)], ...(this.options.files || [])],
symlinks: [[path.join("/lib", appIdentifier, executableName), path.join("/bin", executableName)], ...(this.options.symlinks || [])],
};
return { manifest, buildOptions };
}
}
exports.default = FlatpakTarget;
const flatpakBuilderDefaults = {
runtime: "org.freedesktop.Platform",
runtimeVersion: "20.08",
sdk: "org.freedesktop.Sdk",
base: "org.electronjs.Electron2.BaseApp",
baseVersion: "20.08",
finishArgs: [
// Wayland/X11 Rendering
"--socket=wayland",
"--socket=x11",
"--share=ipc",
// Open GL
"--device=dri",
// Audio output
"--socket=pulseaudio",
// Read/write home directory access
"--filesystem=home",
// Allow communication with network
"--share=network",
// System notifications with libnotify
"--talk-name=org.freedesktop.Notifications",
],
};
function getElectronWrapperScript(executableName, useWaylandFlags) {
return useWaylandFlags
? `#!/bin/sh
export TMPDIR="$XDG_RUNTIME_DIR/app/$FLATPAK_ID"
if [ "\${XDG_SESSION_TYPE}" == "wayland" ]; then
zypak-wrapper "${executableName}" --enable-features=UseOzonePlatform --ozone-platform=wayland "$@"
else
zypak-wrapper "${executableName}" "$@"
fi
`
: `#!/bin/sh
export TMPDIR="$XDG_RUNTIME_DIR/app/$FLATPAK_ID"
zypak-wrapper "${executableName}" "$@"
`;
}
function filterFlatpakAppIdentifier(identifier) {
// Remove special characters and allow only alphanumeric (A-Z,a-z,0-9), underscore (_), and period (.)
// Flatpak documentation: https://docs.flatpak.org/en/latest/conventions.html#application-ids
return identifier.replace(/-/g, "_").replace(/[^a-zA-Z0-9._]/g, "");
}
//# sourceMappingURL=FlatpakTarget.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,18 @@
import { Arch } from "builder-util";
import { Target } from "../core";
import { LinuxPackager } from "../linuxPackager";
import { LinuxTargetSpecificOptions } from "../options/linuxOptions";
import { LinuxTargetHelper } from "./LinuxTargetHelper";
export default class FpmTarget extends Target {
private readonly packager;
private readonly helper;
readonly outDir: string;
readonly options: LinuxTargetSpecificOptions;
private readonly scriptFiles;
constructor(name: string, packager: LinuxPackager, helper: LinuxTargetHelper, outDir: string);
private createScripts;
checkOptions(): Promise<any>;
private computeFpmMetaInfoOptions;
build(appOutDir: string, arch: Arch): Promise<any>;
private supportsAutoUpdate;
}

257
mc_test/node_modules/app-builder-lib/out/targets/FpmTarget.js generated vendored Executable file
View File

@ -0,0 +1,257 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const builder_util_1 = require("builder-util");
const fs_1 = require("builder-util/out/fs");
const fs_extra_1 = require("fs-extra");
const promises_1 = require("fs/promises");
const path = require("path");
const appInfo_1 = require("../appInfo");
const core_1 = require("../core");
const errorMessages = require("../errorMessages");
const appBuilder_1 = require("../util/appBuilder");
const bundledTool_1 = require("../util/bundledTool");
const macosVersion_1 = require("../util/macosVersion");
const pathManager_1 = require("../util/pathManager");
const LinuxTargetHelper_1 = require("./LinuxTargetHelper");
const tools_1 = require("./tools");
const hash_1 = require("../util/hash");
const PublishManager_1 = require("../publish/PublishManager");
const builder_util_2 = require("builder-util");
class FpmTarget extends core_1.Target {
constructor(name, packager, helper, outDir) {
super(name, false);
this.packager = packager;
this.helper = helper;
this.outDir = outDir;
this.options = { ...this.packager.platformSpecificBuildOptions, ...this.packager.config[this.name] };
this.scriptFiles = this.createScripts();
}
async createScripts() {
const defaultTemplatesDir = (0, pathManager_1.getTemplatePath)("linux");
const packager = this.packager;
const templateOptions = {
// old API compatibility
executable: packager.executableName,
sanitizedProductName: packager.appInfo.sanitizedProductName,
productFilename: packager.appInfo.productFilename,
...packager.platformSpecificBuildOptions,
};
function getResource(value, defaultFile) {
if (value == null) {
return path.join(defaultTemplatesDir, defaultFile);
}
return path.resolve(packager.projectDir, value);
}
return await Promise.all([
writeConfigFile(packager.info.tempDirManager, getResource(this.options.afterInstall, "after-install.tpl"), templateOptions),
writeConfigFile(packager.info.tempDirManager, getResource(this.options.afterRemove, "after-remove.tpl"), templateOptions),
]);
}
checkOptions() {
return this.computeFpmMetaInfoOptions();
}
async computeFpmMetaInfoOptions() {
var _a;
const packager = this.packager;
const projectUrl = await packager.appInfo.computePackageUrl();
const errors = [];
if (projectUrl == null) {
errors.push("Please specify project homepage, see https://electron.build/configuration/configuration#Metadata-homepage");
}
const options = this.options;
let author = options.maintainer;
if (author == null) {
const a = packager.info.metadata.author;
if (a == null || a.email == null) {
errors.push(errorMessages.authorEmailIsMissed);
}
else {
author = `${a.name} <${a.email}>`;
}
}
if (errors.length > 0) {
throw new Error(errors.join("\n\n"));
}
return {
name: (_a = options.packageName) !== null && _a !== void 0 ? _a : this.packager.appInfo.linuxPackageName,
maintainer: author,
url: projectUrl,
vendor: options.vendor || author,
};
}
async build(appOutDir, arch) {
var _a;
const target = this.name;
// tslint:disable:no-invalid-template-strings
let nameFormat = "${name}-${version}-${arch}.${ext}";
let isUseArchIfX64 = false;
if (target === "deb") {
nameFormat = "${name}_${version}_${arch}.${ext}";
isUseArchIfX64 = true;
}
else if (target === "rpm") {
nameFormat = "${name}-${version}.${arch}.${ext}";
isUseArchIfX64 = true;
}
const packager = this.packager;
const artifactName = packager.expandArtifactNamePattern(this.options, target, arch, nameFormat, !isUseArchIfX64);
const artifactPath = path.join(this.outDir, artifactName);
await packager.info.callArtifactBuildStarted({
targetPresentableName: target,
file: artifactPath,
arch,
});
await (0, fs_1.unlinkIfExists)(artifactPath);
if (packager.packagerOptions.prepackaged != null) {
await (0, promises_1.mkdir)(this.outDir, { recursive: true });
}
const publishConfig = this.supportsAutoUpdate(target)
? await (0, PublishManager_1.getAppUpdatePublishConfiguration)(packager, arch, false /* in any case validation will be done on publish */)
: null;
if (publishConfig != null) {
const linuxDistType = this.packager.packagerOptions.prepackaged || path.join(this.outDir, `linux${(0, builder_util_1.getArchSuffix)(arch)}-unpacked`);
const resourceDir = packager.getResourcesDir(linuxDistType);
builder_util_1.log.info({ resourceDir }, `adding autoupdate files for: ${target}. (Beta feature)`);
await (0, fs_extra_1.outputFile)(path.join(resourceDir, "app-update.yml"), (0, builder_util_1.serializeToYaml)(publishConfig));
// Extra file needed for auto-updater to detect installation method
await (0, fs_extra_1.outputFile)(path.join(resourceDir, "package-type"), target);
}
const scripts = await this.scriptFiles;
const appInfo = packager.appInfo;
const options = this.options;
const synopsis = options.synopsis;
const args = [
"--architecture",
(0, builder_util_1.toLinuxArchString)(arch, target),
"--after-install",
scripts[0],
"--after-remove",
scripts[1],
"--description",
(0, appInfo_1.smarten)(target === "rpm" ? this.helper.getDescription(options) : `${synopsis || ""}\n ${this.helper.getDescription(options)}`),
"--version",
this.helper.getSanitizedVersion(target),
"--package",
artifactPath,
];
(0, appBuilder_1.objectToArgs)(args, (await this.computeFpmMetaInfoOptions()));
const packageCategory = options.packageCategory;
if (packageCategory != null) {
args.push("--category", packageCategory);
}
if (target === "deb") {
args.push("--deb-priority", (_a = options.priority) !== null && _a !== void 0 ? _a : "optional");
}
else if (target === "rpm") {
if (synopsis != null) {
args.push("--rpm-summary", (0, appInfo_1.smarten)(synopsis));
}
}
const fpmConfiguration = {
args,
target,
};
if (options.compression != null) {
fpmConfiguration.compression = options.compression;
}
// noinspection JSDeprecatedSymbols
const depends = options.depends;
if (depends != null) {
if (Array.isArray(depends)) {
fpmConfiguration.customDepends = depends;
}
else {
// noinspection SuspiciousTypeOfGuard
if (typeof depends === "string") {
fpmConfiguration.customDepends = [depends];
}
else {
throw new Error(`depends must be Array or String, but specified as: ${depends}`);
}
}
}
if (target === "deb") {
const recommends = options.recommends;
if (recommends) {
fpmConfiguration.customRecommends = (0, builder_util_1.asArray)(recommends);
}
}
(0, builder_util_1.use)(packager.info.metadata.license, it => args.push("--license", it));
(0, builder_util_1.use)(appInfo.buildNumber, it => args.push("--iteration",
// dashes are not supported for iteration in older versions of fpm
// https://github.com/jordansissel/fpm/issues/1833
it.split("-").join("_")));
(0, builder_util_1.use)(options.fpm, it => args.push(...it));
args.push(`${appOutDir}/=${LinuxTargetHelper_1.installPrefix}/${appInfo.sanitizedProductName}`);
for (const icon of await this.helper.icons) {
const extWithDot = path.extname(icon.file);
const sizeName = extWithDot === ".svg" ? "scalable" : `${icon.size}x${icon.size}`;
args.push(`${icon.file}=/usr/share/icons/hicolor/${sizeName}/apps/${packager.executableName}${extWithDot}`);
}
const mimeTypeFilePath = await this.helper.mimeTypeFiles;
if (mimeTypeFilePath != null) {
args.push(`${mimeTypeFilePath}=/usr/share/mime/packages/${packager.executableName}.xml`);
}
const desktopFilePath = await this.helper.writeDesktopEntry(this.options);
args.push(`${desktopFilePath}=/usr/share/applications/${packager.executableName}.desktop`);
if (packager.packagerOptions.effectiveOptionComputed != null && (await packager.packagerOptions.effectiveOptionComputed([args, desktopFilePath]))) {
return;
}
const env = {
...process.env,
SZA_PATH: await (0, builder_util_2.getPath7za)(),
SZA_COMPRESSION_LEVEL: packager.compression === "store" ? "0" : "9",
};
// rpmbuild wants directory rpm with some default config files. Even if we can use dylibbundler, path to such config files are not changed (we need to replace in the binary)
// so, for now, brew install rpm is still required.
if (target !== "rpm" && (await (0, macosVersion_1.isMacOsSierra)())) {
const linuxToolsPath = await (0, tools_1.getLinuxToolsPath)();
Object.assign(env, {
PATH: (0, bundledTool_1.computeEnv)(process.env.PATH, [path.join(linuxToolsPath, "bin")]),
DYLD_LIBRARY_PATH: (0, bundledTool_1.computeEnv)(process.env.DYLD_LIBRARY_PATH, [path.join(linuxToolsPath, "lib")]),
});
}
await (0, builder_util_1.executeAppBuilder)(["fpm", "--configuration", JSON.stringify(fpmConfiguration)], undefined, { env });
let info = {
file: artifactPath,
target: this,
arch,
packager,
};
if (publishConfig != null) {
info = {
...info,
safeArtifactName: packager.computeSafeArtifactName(artifactName, target, arch, !isUseArchIfX64),
isWriteUpdateInfo: true,
updateInfo: {
sha512: await (0, hash_1.hashFile)(artifactPath),
size: (await (0, fs_extra_1.stat)(artifactPath)).size,
},
};
}
await packager.info.callArtifactBuildCompleted(info);
}
supportsAutoUpdate(target) {
return ["deb", "rpm"].includes(target);
}
}
exports.default = FpmTarget;
async function writeConfigFile(tmpDir, templatePath, options) {
//noinspection JSUnusedLocalSymbols
function replacer(match, p1) {
if (p1 in options) {
return options[p1];
}
else {
throw new Error(`Macro ${p1} is not defined`);
}
}
const config = (await (0, promises_1.readFile)(templatePath, "utf8")).replace(/\${([a-zA-Z]+)}/g, replacer).replace(/<%=([a-zA-Z]+)%>/g, (match, p1) => {
builder_util_1.log.warn("<%= varName %> is deprecated, please use ${varName} instead");
return replacer(match, p1.trim());
});
const outputPath = await tmpDir.getTempFile({ suffix: path.basename(templatePath, ".tpl") });
await (0, fs_extra_1.outputFile)(outputPath, config);
return outputPath;
}
//# sourceMappingURL=FpmTarget.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,23 @@
import { LinuxPackager } from "../linuxPackager";
import { LinuxTargetSpecificOptions } from "../options/linuxOptions";
import { IconInfo } from "../platformPackager";
export declare const installPrefix = "/opt";
export declare class LinuxTargetHelper {
private packager;
private readonly iconPromise;
private readonly mimeTypeFilesPromise;
maxIconPath: string | null;
constructor(packager: LinuxPackager);
get icons(): Promise<Array<IconInfo>>;
get mimeTypeFiles(): Promise<string | null>;
private computeMimeTypeFiles;
private computeDesktopIcons;
getDescription(options: LinuxTargetSpecificOptions): string;
getSanitizedVersion(target: string): string;
writeDesktopEntry(targetSpecificOptions: LinuxTargetSpecificOptions, exec?: string, destination?: string | null, extra?: {
[key: string]: string;
}): Promise<string>;
computeDesktopEntry(targetSpecificOptions: LinuxTargetSpecificOptions, exec?: string, extra?: {
[key: string]: string;
}): Promise<string>;
}

View File

@ -0,0 +1,177 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.LinuxTargetHelper = exports.installPrefix = void 0;
const builder_util_1 = require("builder-util");
const fs_extra_1 = require("fs-extra");
const lazy_val_1 = require("lazy-val");
const path_1 = require("path");
exports.installPrefix = "/opt";
class LinuxTargetHelper {
constructor(packager) {
this.packager = packager;
this.iconPromise = new lazy_val_1.Lazy(() => this.computeDesktopIcons());
this.mimeTypeFilesPromise = new lazy_val_1.Lazy(() => this.computeMimeTypeFiles());
this.maxIconPath = null;
}
get icons() {
return this.iconPromise.value;
}
get mimeTypeFiles() {
return this.mimeTypeFilesPromise.value;
}
async computeMimeTypeFiles() {
const items = [];
for (const fileAssociation of this.packager.fileAssociations) {
if (!fileAssociation.mimeType) {
continue;
}
const data = `<mime-type type="${fileAssociation.mimeType}">
<glob pattern="*.${fileAssociation.ext}"/>
${fileAssociation.description ? `<comment>${fileAssociation.description}</comment>` : ""}
<icon name="x-office-document" />
</mime-type>`;
items.push(data);
}
if (items.length === 0) {
return null;
}
const file = await this.packager.getTempFile(".xml");
await (0, fs_extra_1.outputFile)(file, '<?xml version="1.0" encoding="utf-8"?>\n<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">\n' + items.join("\n") + "\n</mime-info>");
return file;
}
// must be name without spaces and other special characters, but not product name used
async computeDesktopIcons() {
var _a, _b, _c;
const packager = this.packager;
const { platformSpecificBuildOptions, config } = packager;
const sources = [platformSpecificBuildOptions.icon, (_b = (_a = config.mac) === null || _a === void 0 ? void 0 : _a.icon) !== null && _b !== void 0 ? _b : config.icon].filter(str => !!str);
// If no explicit sources are defined, fallback to buildResources directory, then default framework icon
let fallbackSources = [...(0, builder_util_1.asArray)(packager.getDefaultFrameworkIcon())];
const buildResources = (_c = config.directories) === null || _c === void 0 ? void 0 : _c.buildResources;
if (buildResources && (await (0, builder_util_1.exists)((0, path_1.join)(buildResources, "icons")))) {
fallbackSources = [buildResources, ...fallbackSources];
}
// need to put here and not as default because need to resolve image size
const result = await packager.resolveIcon(sources, fallbackSources, "set");
this.maxIconPath = result[result.length - 1].file;
return result;
}
getDescription(options) {
return options.description || this.packager.appInfo.description;
}
getSanitizedVersion(target) {
const { appInfo: { version }, } = this.packager;
switch (target) {
case "pacman":
return version.replace(/-/g, "_");
case "rpm":
case "deb":
return version.replace(/-/g, "~");
default:
return version;
}
}
async writeDesktopEntry(targetSpecificOptions, exec, destination, extra) {
const data = await this.computeDesktopEntry(targetSpecificOptions, exec, extra);
const file = destination || (await this.packager.getTempFile(`${this.packager.appInfo.productFilename}.desktop`));
await (0, fs_extra_1.outputFile)(file, data);
return file;
}
computeDesktopEntry(targetSpecificOptions, exec, extra) {
if (exec != null && exec.length === 0) {
throw new Error("Specified exec is empty");
}
// https://github.com/electron-userland/electron-builder/issues/3418
if (targetSpecificOptions.desktop != null && targetSpecificOptions.desktop.Exec != null) {
throw new Error("Please specify executable name as linux.executableName instead of linux.desktop.Exec");
}
const packager = this.packager;
const appInfo = packager.appInfo;
const executableArgs = targetSpecificOptions.executableArgs;
if (exec == null) {
exec = `${exports.installPrefix}/${appInfo.sanitizedProductName}/${packager.executableName}`;
if (!/^[/0-9A-Za-z._-]+$/.test(exec)) {
exec = `"${exec}"`;
}
if (executableArgs) {
exec += " ";
exec += executableArgs.join(" ");
}
// https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#exec-variables
const execCodes = ["%f", "%u", "%F", "%U"];
if (executableArgs == null || executableArgs.findIndex(arg => execCodes.includes(arg)) === -1) {
exec += " %U";
}
}
const desktopMeta = {
Name: appInfo.productName,
Exec: exec,
Terminal: "false",
Type: "Application",
Icon: packager.executableName,
// https://askubuntu.com/questions/367396/what-represent-the-startupwmclass-field-of-a-desktop-file
// must be set to package.json name (because it is Electron set WM_CLASS)
// to get WM_CLASS of running window: xprop WM_CLASS
// StartupWMClass doesn't work for unicode
// https://github.com/electron/electron/blob/2-0-x/atom/browser/native_window_views.cc#L226
StartupWMClass: appInfo.productName,
...extra,
...targetSpecificOptions.desktop,
};
const description = this.getDescription(targetSpecificOptions);
if (!(0, builder_util_1.isEmptyOrSpaces)(description)) {
desktopMeta.Comment = description;
}
const mimeTypes = (0, builder_util_1.asArray)(targetSpecificOptions.mimeTypes);
for (const fileAssociation of packager.fileAssociations) {
if (fileAssociation.mimeType != null) {
mimeTypes.push(fileAssociation.mimeType);
}
}
for (const protocol of (0, builder_util_1.asArray)(packager.config.protocols).concat((0, builder_util_1.asArray)(packager.platformSpecificBuildOptions.protocols))) {
for (const scheme of (0, builder_util_1.asArray)(protocol.schemes)) {
mimeTypes.push(`x-scheme-handler/${scheme}`);
}
}
if (mimeTypes.length !== 0) {
desktopMeta.MimeType = mimeTypes.join(";") + ";";
}
let category = targetSpecificOptions.category;
if ((0, builder_util_1.isEmptyOrSpaces)(category)) {
const macCategory = (packager.config.mac || {}).category;
if (macCategory != null) {
category = macToLinuxCategory[macCategory];
}
if (category == null) {
// https://github.com/develar/onshape-desktop-shell/issues/48
if (macCategory != null) {
builder_util_1.log.warn({ macCategory }, "cannot map macOS category to Linux. If possible mapping is known for you, please file issue to add it.");
}
builder_util_1.log.warn({
reason: "linux.category is not set and cannot map from macOS",
docs: "https://www.electron.build/configuration/linux",
}, 'application Linux category is set to default "Utility"');
category = "Utility";
}
}
desktopMeta.Categories = `${category}${category.endsWith(";") ? "" : ";"}`;
let data = `[Desktop Entry]`;
for (const name of Object.keys(desktopMeta)) {
data += `\n${name}=${desktopMeta[name]}`;
}
data += "\n";
return Promise.resolve(data);
}
}
exports.LinuxTargetHelper = LinuxTargetHelper;
const macToLinuxCategory = {
"public.app-category.graphics-design": "Graphics",
"public.app-category.developer-tools": "Development",
"public.app-category.education": "Education",
"public.app-category.games": "Game",
"public.app-category.video": "Video;AudioVideo",
"public.app-category.utilities": "Utility",
"public.app-category.social-networking": "Network;Chat",
"public.app-category.finance": "Office;Finance",
};
//# sourceMappingURL=LinuxTargetHelper.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,27 @@
import { Arch } from "builder-util";
import { Lazy } from "lazy-val";
import { MsiOptions } from "../";
import { Target } from "../core";
import { FinalCommonWindowsInstallerOptions } from "../options/CommonWindowsInstallerConfiguration";
import { VmManager } from "../vm/vm";
import { WinPackager } from "../winPackager";
export default class MsiTarget extends Target {
protected readonly packager: WinPackager;
readonly outDir: string;
protected readonly vm: VmManager;
readonly options: MsiOptions;
constructor(packager: WinPackager, outDir: string, name?: string, isAsyncSupported?: boolean);
protected projectTemplate: Lazy<(data: any) => string>;
/**
* A product-specific string that can be used in an [MSI Identifier](https://docs.microsoft.com/en-us/windows/win32/msi/identifier).
*/
private get productMsiIdPrefix();
protected get iconId(): string;
protected get upgradeCode(): string;
build(appOutDir: string, arch: Arch): Promise<void>;
private light;
private getCommonWixArgs;
protected writeManifest(appOutDir: string, wixArch: Arch, commonOptions: FinalCommonWindowsInstallerOptions): Promise<string>;
protected getBaseOptions(commonOptions: FinalCommonWindowsInstallerOptions): Promise<any>;
private computeFileDeclaration;
}

257
mc_test/node_modules/app-builder-lib/out/targets/MsiTarget.js generated vendored Executable file
View File

@ -0,0 +1,257 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const bluebird_lst_1 = require("bluebird-lst");
const builder_util_1 = require("builder-util");
const builder_util_runtime_1 = require("builder-util-runtime");
const binDownload_1 = require("../binDownload");
const fs_1 = require("builder-util/out/fs");
const crypto_1 = require("crypto");
const ejs = require("ejs");
const promises_1 = require("fs/promises");
const lazy_val_1 = require("lazy-val");
const path = require("path");
const core_1 = require("../core");
const CommonWindowsInstallerConfiguration_1 = require("../options/CommonWindowsInstallerConfiguration");
const platformPackager_1 = require("../platformPackager");
const pathManager_1 = require("../util/pathManager");
const vm_1 = require("../vm/vm");
const WineVm_1 = require("../vm/WineVm");
const targetUtil_1 = require("./targetUtil");
const ELECTRON_BUILDER_UPGRADE_CODE_NS_UUID = builder_util_runtime_1.UUID.parse("d752fe43-5d44-44d5-9fc9-6dd1bf19d5cc");
const ROOT_DIR_ID = "APPLICATIONFOLDER";
// WiX doesn't support Mono, so, dontnet462 is required to be installed for wine (preinstalled in our bundled wine)
class MsiTarget extends core_1.Target {
constructor(packager, outDir, name = "msi", isAsyncSupported = true) {
super(name, isAsyncSupported);
this.packager = packager;
this.outDir = outDir;
this.vm = process.platform === "win32" ? new vm_1.VmManager() : new WineVm_1.WineVmManager();
this.options = (0, builder_util_1.deepAssign)(this.packager.platformSpecificBuildOptions, this.packager.config.msi);
this.projectTemplate = new lazy_val_1.Lazy(async () => {
const template = (await (0, promises_1.readFile)(path.join((0, pathManager_1.getTemplatePath)(this.name), "template.xml"), "utf8"))
.replace(/{{/g, "<%")
.replace(/}}/g, "%>")
.replace(/\${([^}]+)}/g, "<%=$1%>");
return ejs.compile(template);
});
}
/**
* A product-specific string that can be used in an [MSI Identifier](https://docs.microsoft.com/en-us/windows/win32/msi/identifier).
*/
get productMsiIdPrefix() {
const sanitizedId = this.packager.appInfo.productFilename.replace(/[^\w.]/g, "").replace(/^[^A-Za-z_]+/, "");
return sanitizedId.length > 0 ? sanitizedId : "App" + this.upgradeCode.replace(/-/g, "");
}
get iconId() {
return `${this.productMsiIdPrefix}Icon.exe`;
}
get upgradeCode() {
return (this.options.upgradeCode || builder_util_runtime_1.UUID.v5(this.packager.appInfo.id, ELECTRON_BUILDER_UPGRADE_CODE_NS_UUID)).toUpperCase();
}
async build(appOutDir, arch) {
const packager = this.packager;
const artifactName = packager.expandArtifactBeautyNamePattern(this.options, "msi", arch);
const artifactPath = path.join(this.outDir, artifactName);
await packager.info.callArtifactBuildStarted({
targetPresentableName: "MSI",
file: artifactPath,
arch,
});
const stageDir = await (0, targetUtil_1.createStageDir)(this, packager, arch);
const vm = this.vm;
const commonOptions = (0, CommonWindowsInstallerConfiguration_1.getEffectiveOptions)(this.options, this.packager);
// wix 4.0.0.5512.2 doesn't support the arm64 architecture so default to x64 when building for arm64.
// This will result in an x64 MSI installer that installs an arm64 version of the application. This is a
// stopgap until the electron-builder-binaries wix version is upgraded to a version that supports arm64:
// https://github.com/electron-userland/electron-builder/issues/6077
const wixArch = arch == builder_util_1.Arch.arm64 ? builder_util_1.Arch.x64 : arch;
const projectFile = stageDir.getTempFile("project.wxs");
const objectFiles = ["project.wixobj"];
await (0, promises_1.writeFile)(projectFile, await this.writeManifest(appOutDir, wixArch, commonOptions));
await packager.info.callMsiProjectCreated(projectFile);
// noinspection SpellCheckingInspection
const vendorPath = await (0, binDownload_1.getBinFromUrl)("wix", "4.0.0.5512.2", "/X5poahdCc3199Vt6AP7gluTlT1nxi9cbbHhZhCMEu+ngyP1LiBMn+oZX7QAZVaKeBMc2SjVp7fJqNLqsUnPNQ==");
// noinspection SpellCheckingInspection
const candleArgs = ["-arch", wixArch === builder_util_1.Arch.ia32 ? "x86" : "x64", `-dappDir=${vm.toVmFile(appOutDir)}`].concat(this.getCommonWixArgs());
candleArgs.push("project.wxs");
await vm.exec(vm.toVmFile(path.join(vendorPath, "candle.exe")), candleArgs, {
cwd: stageDir.dir,
});
await this.light(objectFiles, vm, artifactPath, appOutDir, vendorPath, stageDir.dir);
await stageDir.cleanup();
await packager.sign(artifactPath);
await packager.info.callArtifactBuildCompleted({
file: artifactPath,
packager,
arch,
safeArtifactName: packager.computeSafeArtifactName(artifactName, "msi"),
target: this,
isWriteUpdateInfo: false,
});
}
async light(objectFiles, vm, artifactPath, appOutDir, vendorPath, tempDir) {
// noinspection SpellCheckingInspection
const lightArgs = [
"-out",
vm.toVmFile(artifactPath),
"-v",
// https://github.com/wixtoolset/issues/issues/5169
"-spdb",
// https://sourceforge.net/p/wix/bugs/2405/
// error LGHT1076 : ICE61: This product should remove only older versions of itself. The Maximum version is not less than the current product. (1.1.0.42 1.1.0.42)
"-sw1076",
`-dappDir=${vm.toVmFile(appOutDir)}`,
// "-dcl:high",
].concat(this.getCommonWixArgs());
// http://windows-installer-xml-wix-toolset.687559.n2.nabble.com/Build-3-5-2229-0-give-me-the-following-error-error-LGHT0216-An-unexpected-Win32-exception-with-errorn-td5707443.html
if (process.platform !== "win32") {
// noinspection SpellCheckingInspection
lightArgs.push("-sval");
}
if (this.options.oneClick === false) {
lightArgs.push("-ext", "WixUIExtension");
}
// objectFiles - only filenames, we set current directory to our temp stage dir
lightArgs.push(...objectFiles);
await vm.exec(vm.toVmFile(path.join(vendorPath, "light.exe")), lightArgs, {
cwd: tempDir,
});
}
getCommonWixArgs() {
const args = ["-pedantic"];
if (this.options.warningsAsErrors !== false) {
args.push("-wx");
}
if (this.options.additionalWixArgs != null) {
args.push(...this.options.additionalWixArgs);
}
return args;
}
async writeManifest(appOutDir, wixArch, commonOptions) {
const appInfo = this.packager.appInfo;
const { files, dirs } = await this.computeFileDeclaration(appOutDir);
const options = this.options;
return (await this.projectTemplate.value)({
...(await this.getBaseOptions(commonOptions)),
isCreateDesktopShortcut: commonOptions.isCreateDesktopShortcut !== CommonWindowsInstallerConfiguration_1.DesktopShortcutCreationPolicy.NEVER,
isRunAfterFinish: options.runAfterFinish !== false,
// https://stackoverflow.com/questions/1929038/compilation-error-ice80-the-64bitcomponent-uses-32bitdirectory
programFilesId: wixArch === builder_util_1.Arch.x64 ? "ProgramFiles64Folder" : "ProgramFilesFolder",
// wix in the name because special wix format can be used in the name
installationDirectoryWixName: (0, targetUtil_1.getWindowsInstallationDirName)(appInfo, commonOptions.isAssisted || commonOptions.isPerMachine === true),
dirs,
files,
});
}
async getBaseOptions(commonOptions) {
const appInfo = this.packager.appInfo;
const iconPath = await this.packager.getIconPath();
const compression = this.packager.compression;
const companyName = appInfo.companyName;
if (!companyName) {
builder_util_1.log.warn(`Manufacturer is not set for MSI — please set "author" in the package.json`);
}
return {
...commonOptions,
iconPath: iconPath == null ? null : this.vm.toVmFile(iconPath),
iconId: this.iconId,
compressionLevel: compression === "store" ? "none" : "high",
version: appInfo.getVersionInWeirdWindowsForm(),
productName: appInfo.productName,
upgradeCode: this.upgradeCode,
manufacturer: companyName || appInfo.productName,
appDescription: appInfo.description,
};
}
async computeFileDeclaration(appOutDir) {
const appInfo = this.packager.appInfo;
let isRootDirAddedToRemoveTable = false;
const dirNames = new Set();
const dirs = [];
const fileSpace = " ".repeat(6);
const commonOptions = (0, CommonWindowsInstallerConfiguration_1.getEffectiveOptions)(this.options, this.packager);
const files = await bluebird_lst_1.default.map((0, fs_1.walk)(appOutDir), file => {
const packagePath = file.substring(appOutDir.length + 1);
const lastSlash = packagePath.lastIndexOf(path.sep);
const fileName = lastSlash > 0 ? packagePath.substring(lastSlash + 1) : packagePath;
let directoryId = null;
let dirName = "";
// Wix Directory.FileSource doesn't work - https://stackoverflow.com/questions/21519388/wix-filesource-confusion
if (lastSlash > 0) {
// This Name attribute may also define multiple directories using the inline directory syntax.
// For example, "ProgramFilesFolder:\My Company\My Product\bin" would create a reference to a Directory element with Id="ProgramFilesFolder" then create directories named "My Company" then "My Product" then "bin" nested beneath each other.
// This syntax is a shortcut to defining each directory in an individual Directory element.
dirName = packagePath.substring(0, lastSlash);
// https://github.com/electron-userland/electron-builder/issues/3027
directoryId = "d" + (0, crypto_1.createHash)("md5").update(dirName).digest("base64").replace(/\//g, "_").replace(/\+/g, ".").replace(/=+$/, "");
if (!dirNames.has(dirName)) {
dirNames.add(dirName);
dirs.push(`<Directory Id="${directoryId}" Name="${ROOT_DIR_ID}:\\${dirName.replace(/\//g, "\\")}\\"/>`);
}
}
else if (!isRootDirAddedToRemoveTable) {
isRootDirAddedToRemoveTable = true;
}
// since RegistryValue can be part of Component, *** *** *** *** *** *** *** *** *** wix cannot auto generate guid
// https://stackoverflow.com/questions/1405100/change-my-component-guid-in-wix
let result = `<Component${directoryId === null ? "" : ` Directory="${directoryId}"`}>`;
result += `\n${fileSpace} <File Name="${xmlAttr(fileName)}" Source="$(var.appDir)${path.sep}${xmlAttr(packagePath)}" ReadOnly="yes" KeyPath="yes"`;
const isMainExecutable = packagePath === `${appInfo.productFilename}.exe`;
if (isMainExecutable) {
result += ' Id="mainExecutable"';
}
else if (directoryId === null) {
result += ` Id="${path.basename(packagePath)}_f"`;
}
const isCreateDesktopShortcut = commonOptions.isCreateDesktopShortcut !== CommonWindowsInstallerConfiguration_1.DesktopShortcutCreationPolicy.NEVER;
if (isMainExecutable && (isCreateDesktopShortcut || commonOptions.isCreateStartMenuShortcut)) {
result += `>\n`;
const shortcutName = commonOptions.shortcutName;
if (isCreateDesktopShortcut) {
result += `${fileSpace} <Shortcut Id="desktopShortcut" Directory="DesktopFolder" Name="${xmlAttr(shortcutName)}" WorkingDirectory="APPLICATIONFOLDER" Advertise="yes" Icon="${this.iconId}"/>\n`;
}
const hasMenuCategory = commonOptions.menuCategory != null;
const startMenuShortcutDirectoryId = hasMenuCategory ? "AppProgramMenuDir" : "ProgramMenuFolder";
if (commonOptions.isCreateStartMenuShortcut) {
if (hasMenuCategory) {
dirs.push(`<Directory Id="${startMenuShortcutDirectoryId}" Name="ProgramMenuFolder:\\${commonOptions.menuCategory}\\"/>`);
}
result += `${fileSpace} <Shortcut Id="startMenuShortcut" Directory="${startMenuShortcutDirectoryId}" Name="${xmlAttr(shortcutName)}" WorkingDirectory="APPLICATIONFOLDER" Advertise="yes" Icon="${this.iconId}">\n`;
result += `${fileSpace} <ShortcutProperty Key="System.AppUserModel.ID" Value="${xmlAttr(this.packager.appInfo.id)}"/>\n`;
result += `${fileSpace} </Shortcut>\n`;
}
result += `${fileSpace}</File>`;
if (hasMenuCategory) {
result += `<RemoveFolder Id="${startMenuShortcutDirectoryId}" Directory="${startMenuShortcutDirectoryId}" On="uninstall"/>\n`;
}
}
else {
result += `/>`;
}
const fileAssociations = this.packager.fileAssociations;
if (isMainExecutable && fileAssociations.length !== 0) {
for (const item of fileAssociations) {
const extensions = (0, builder_util_1.asArray)(item.ext).map(platformPackager_1.normalizeExt);
for (const ext of extensions) {
result += `${fileSpace} <ProgId Id="${this.productMsiIdPrefix}.${ext}" Advertise="yes" Icon="${this.iconId}" ${item.description ? `Description="${item.description}"` : ""}>\n`;
result += `${fileSpace} <Extension Id="${ext}" Advertise="yes">\n`;
result += `${fileSpace} <Verb Id="open" Command="Open with ${xmlAttr(this.packager.appInfo.productName)}" Argument="&quot;%1&quot;"/>\n`;
result += `${fileSpace} </Extension>\n`;
result += `${fileSpace} </ProgId>\n`;
}
}
}
return `${result}\n${fileSpace}</Component>`;
});
return { dirs: listToString(dirs, 2), files: listToString(files, 3) };
}
}
exports.default = MsiTarget;
function listToString(list, indentLevel) {
const space = " ".repeat(indentLevel * 2);
return list.join(`\n${space}`);
}
function xmlAttr(str) {
return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
}
//# sourceMappingURL=MsiTarget.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,19 @@
import { Arch } from "builder-util";
import { MsiWrappedOptions } from "../";
import { FinalCommonWindowsInstallerOptions } from "../options/CommonWindowsInstallerConfiguration";
import { WinPackager } from "../winPackager";
import MsiTarget from "./MsiTarget";
export default class MsiWrappedTarget extends MsiTarget {
readonly outDir: string;
readonly options: MsiWrappedOptions;
/** @private */
private readonly archs;
constructor(packager: WinPackager, outDir: string);
private get productId();
private validatePrerequisites;
build(appOutDir: string, arch: Arch): Promise<any>;
finishBuild(): Promise<any>;
protected get installerFilenamePattern(): string;
private getExeSourcePath;
protected writeManifest(_appOutDir: string, arch: Arch, commonOptions: FinalCommonWindowsInstallerOptions): Promise<string>;
}

View File

@ -0,0 +1,78 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const builder_util_1 = require("builder-util");
const builder_util_runtime_1 = require("builder-util-runtime");
const path = require("path");
const MsiTarget_1 = require("./MsiTarget");
const ELECTRON_MSI_WRAPPED_NS_UUID = builder_util_runtime_1.UUID.parse("467f7bb2-a83c-442f-b776-394d316e8e53");
class MsiWrappedTarget extends MsiTarget_1.default {
constructor(packager, outDir) {
// must be synchronous so it can run after nsis
super(packager, outDir, "msiWrapped", false);
this.outDir = outDir;
this.options = (0, builder_util_1.deepAssign)(this.packager.platformSpecificBuildOptions, this.packager.config.msiWrapped);
/** @private */
this.archs = new Map();
}
get productId() {
// this id is only required to build the installer
// however it serves no purpose as this msi is just
// a wrapper for an exe
return builder_util_runtime_1.UUID.v5(this.packager.appInfo.id, ELECTRON_MSI_WRAPPED_NS_UUID).toUpperCase();
}
validatePrerequisites() {
const config = this.packager.config;
// this target requires nsis to be configured and executed
// as this build re-bundles the nsis executable and wraps it in an msi
if (!config.win || !config.win.target || !Array.isArray(config.win.target)) {
throw new Error("No windows target found!");
}
const target = config.win.target;
const nsisTarget = "nsis";
if (!target
.map((t) => {
const result = typeof t === "string" ? t : t.target;
return result.toLowerCase().trim();
})
.some(t => t === nsisTarget)) {
throw new Error("No nsis target found! Please specify an nsis target");
}
}
build(appOutDir, arch) {
this.archs.set(arch, appOutDir);
return Promise.resolve();
}
finishBuild() {
// this target invokes `build` in `finishBuild` to guarantee
// that the dependent target has already been built
// this also affords us re-usability
const [arch, appOutDir] = this.archs.entries().next().value;
this.validatePrerequisites();
return super.build(appOutDir, arch);
}
get installerFilenamePattern() {
// big assumption is made here for the moment that the pattern didn't change
// tslint:disable:no-invalid-template-strings
return "${productName} Setup ${version}.${ext}";
}
getExeSourcePath(arch) {
const packager = this.packager;
// in this case, we want .exe, this way we can wrap the existing package if it exists
const artifactName = packager.expandArtifactNamePattern(this.options, "exe", arch, this.installerFilenamePattern, false, this.packager.platformSpecificBuildOptions.defaultArch);
const artifactPath = path.join(this.outDir, artifactName);
return artifactPath;
}
async writeManifest(_appOutDir, arch, commonOptions) {
const exeSourcePath = this.getExeSourcePath(arch);
const options = this.options;
return (await this.projectTemplate.value)({
...(await this.getBaseOptions(commonOptions)),
exeSourcePath: exeSourcePath,
productId: this.productId,
impersonate: options.impersonate === true ? "yes" : "no",
wrappedInstallerArgs: options.wrappedInstallerArgs,
});
}
}
exports.default = MsiWrappedTarget;
//# sourceMappingURL=MsiWrappedTarget.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,22 @@
import { CompressionLevel } from "../core";
export interface ArchiveOptions {
compression?: CompressionLevel | null;
/**
* @default false
*/
withoutDir?: boolean;
/**
* @default true
*/
solid?: boolean;
/**
* @default true
*/
isArchiveHeaderCompressed?: boolean;
dictSize?: number;
excluded?: Array<string> | null;
method?: "Copy" | "LZMA" | "Deflate" | "DEFAULT";
isRegularFile?: boolean;
}
export declare function compute7zCompressArgs(format: string, options?: ArchiveOptions): string[];
export declare function computeZipCompressArgs(options?: ArchiveOptions): string[];

196
mc_test/node_modules/app-builder-lib/out/targets/archive.js generated vendored Executable file
View File

@ -0,0 +1,196 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.archive = exports.computeZipCompressArgs = exports.compute7zCompressArgs = exports.tar = void 0;
const builder_util_1 = require("builder-util");
const fs_1 = require("builder-util/out/fs");
const fs_extra_1 = require("fs-extra");
const path = require("path");
const tar_1 = require("tar");
const tools_1 = require("./tools");
const builder_util_2 = require("builder-util");
/** @internal */
async function tar(compression, format, outFile, dirToArchive, isMacApp, tempDirManager) {
const tarFile = await tempDirManager.getTempFile({ suffix: ".tar" });
const tarArgs = {
file: tarFile,
portable: true,
cwd: dirToArchive,
prefix: path.basename(outFile, `.${format}`),
};
let tarDirectory = ".";
if (isMacApp) {
delete tarArgs.prefix;
tarArgs.cwd = path.dirname(dirToArchive);
tarDirectory = path.basename(dirToArchive);
}
await Promise.all([
(0, tar_1.create)(tarArgs, [tarDirectory]),
// remove file before - 7z doesn't overwrite file, but update
(0, fs_1.unlinkIfExists)(outFile),
]);
if (format === "tar.lz") {
// noinspection SpellCheckingInspection
let lzipPath = "lzip";
if (process.platform === "darwin") {
lzipPath = path.join(await (0, tools_1.getLinuxToolsPath)(), "bin", lzipPath);
}
await (0, builder_util_1.exec)(lzipPath, [compression === "store" ? "-1" : "-9", "--keep" /* keep (don't delete) input files */, tarFile]);
// bloody lzip creates file in the same dir where input file with postfix `.lz`, option --output doesn't work
await (0, fs_extra_1.move)(`${tarFile}.lz`, outFile);
return;
}
const args = compute7zCompressArgs(format === "tar.xz" ? "xz" : format === "tar.bz2" ? "bzip2" : "gzip", {
isRegularFile: true,
method: "DEFAULT",
compression,
});
args.push(outFile, tarFile);
await (0, builder_util_1.exec)(await (0, builder_util_2.getPath7za)(), args, {
cwd: path.dirname(dirToArchive),
}, builder_util_1.debug7z.enabled);
}
exports.tar = tar;
function compute7zCompressArgs(format, options = {}) {
let storeOnly = options.compression === "store";
const args = debug7zArgs("a");
let isLevelSet = false;
if (process.env.ELECTRON_BUILDER_COMPRESSION_LEVEL != null) {
storeOnly = false;
args.push(`-mx=${process.env.ELECTRON_BUILDER_COMPRESSION_LEVEL}`);
isLevelSet = true;
}
const isZip = format === "zip";
if (!storeOnly) {
if (isZip && options.compression === "maximum") {
// http://superuser.com/a/742034
args.push("-mfb=258", "-mpass=15");
}
if (!isLevelSet) {
// https://github.com/electron-userland/electron-builder/pull/3032
args.push("-mx=" + (!isZip || options.compression === "maximum" ? "9" : "7"));
}
}
if (options.dictSize != null) {
args.push(`-md=${options.dictSize}m`);
}
// https://sevenzip.osdn.jp/chm/cmdline/switches/method.htm#7Z
// https://stackoverflow.com/questions/27136783/7zip-produces-different-output-from-identical-input
// tc and ta are off by default, but to be sure, we explicitly set it to off
// disable "Stores NTFS timestamps for files: Modification time, Creation time, Last access time." to produce the same archive for the same data
if (!options.isRegularFile) {
args.push("-mtc=off");
}
if (format === "7z" || format.endsWith(".7z")) {
if (options.solid === false) {
args.push("-ms=off");
}
if (options.isArchiveHeaderCompressed === false) {
args.push("-mhc=off");
}
// https://www.7-zip.org/7z.html
// Filters: BCJ, BCJ2, ARM, ARMT, IA64, PPC, SPARC, ...
if (process.env.ELECTRON_BUILDER_7Z_FILTER) {
args.push(`-mf=${process.env.ELECTRON_BUILDER_7Z_FILTER}`);
}
// args valid only for 7z
// -mtm=off disable "Stores last Modified timestamps for files."
args.push("-mtm=off", "-mta=off");
}
if (options.method != null) {
if (options.method !== "DEFAULT") {
args.push(`-mm=${options.method}`);
}
}
else if (isZip || storeOnly) {
args.push(`-mm=${storeOnly ? "Copy" : "Deflate"}`);
}
if (isZip) {
// -mcu switch: 7-Zip uses UTF-8, if there are non-ASCII symbols.
// because default mode: 7-Zip uses UTF-8, if the local code page doesn't contain required symbols.
// but archive should be the same regardless where produced
args.push("-mcu");
}
return args;
}
exports.compute7zCompressArgs = compute7zCompressArgs;
function computeZipCompressArgs(options = {}) {
let storeOnly = options.compression === "store";
// do not deref symlinks
const args = ["-q", "-r", "-y"];
if (builder_util_1.debug7z.enabled) {
args.push("-v");
}
if (process.env.ELECTRON_BUILDER_COMPRESSION_LEVEL != null) {
storeOnly = false;
args.push(`-${process.env.ELECTRON_BUILDER_COMPRESSION_LEVEL}`);
}
else if (!storeOnly) {
// https://github.com/electron-userland/electron-builder/pull/3032
args.push("-" + (options.compression === "maximum" ? "9" : "7"));
}
if (options.dictSize != null) {
builder_util_1.log.warn({ distSize: options.dictSize }, `ignoring unsupported option`);
}
// do not save extra file attributes (Extended Attributes on OS/2, uid/gid and file times on Unix)
if (!options.isRegularFile) {
args.push("-X");
}
if (options.method != null) {
if (options.method !== "DEFAULT") {
builder_util_1.log.warn({ method: options.method }, `ignoring unsupported option`);
}
}
else {
args.push("-Z", storeOnly ? "store" : "deflate");
}
return args;
}
exports.computeZipCompressArgs = computeZipCompressArgs;
// 7z is very fast, so, use ultra compression
/** @internal */
async function archive(format, outFile, dirToArchive, options = {}) {
const outFileStat = await (0, fs_1.statOrNull)(outFile);
const dirStat = await (0, fs_1.statOrNull)(dirToArchive);
if (outFileStat && dirStat && outFileStat.mtime > dirStat.mtime) {
builder_util_1.log.info({ reason: "Archive file is up to date", outFile }, `skipped archiving`);
return outFile;
}
let use7z = true;
if (process.platform === "darwin" && format === "zip" && dirToArchive.normalize("NFC") !== dirToArchive) {
builder_util_1.log.warn({ reason: "7z doesn't support NFD-normalized filenames" }, `using zip`);
use7z = false;
}
const args = use7z ? compute7zCompressArgs(format, options) : computeZipCompressArgs(options);
// remove file before - 7z and zip doesn't overwrite file, but update
await (0, fs_1.unlinkIfExists)(outFile);
args.push(outFile, options.withoutDir ? "." : path.basename(dirToArchive));
if (options.excluded != null) {
for (const mask of options.excluded) {
args.push(use7z ? `-xr!${mask}` : `-x${mask}`);
}
}
try {
const binary = use7z ? await (0, builder_util_2.getPath7za)() : "zip";
await (0, builder_util_1.exec)(binary, args, {
cwd: options.withoutDir ? dirToArchive : path.dirname(dirToArchive),
}, builder_util_1.debug7z.enabled);
}
catch (e) {
if (e.code === "ENOENT" && !(await (0, fs_1.exists)(dirToArchive))) {
throw new Error(`Cannot create archive: "${dirToArchive}" doesn't exist`);
}
else {
throw e;
}
}
return outFile;
}
exports.archive = archive;
function debug7zArgs(command) {
const args = [command, "-bd"];
if (builder_util_1.debug7z.enabled) {
args.push("-bb");
}
return args;
}
//# sourceMappingURL=archive.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,15 @@
import { BlockMapDataHolder, PackageFileInfo } from "builder-util-runtime";
import { Target } from "../core";
import { PlatformPackager } from "../platformPackager";
import { ArchiveOptions } from "./archive";
export declare const BLOCK_MAP_FILE_SUFFIX = ".blockmap";
export declare function createNsisWebDifferentialUpdateInfo(artifactPath: string, packageFiles: {
[arch: string]: PackageFileInfo;
}): {
packages: {
[arch: string]: PackageFileInfo;
};
} | null;
export declare function configureDifferentialAwareArchiveOptions(archiveOptions: ArchiveOptions): ArchiveOptions;
export declare function appendBlockmap(file: string): Promise<BlockMapDataHolder>;
export declare function createBlockmap(file: string, target: Target, packager: PlatformPackager<any>, safeArtifactName: string | null): Promise<BlockMapDataHolder>;

View File

@ -0,0 +1,80 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createBlockmap = exports.appendBlockmap = exports.configureDifferentialAwareArchiveOptions = exports.createNsisWebDifferentialUpdateInfo = exports.BLOCK_MAP_FILE_SUFFIX = void 0;
const builder_util_1 = require("builder-util");
const path = require("path");
const appBuilder_1 = require("../util/appBuilder");
exports.BLOCK_MAP_FILE_SUFFIX = ".blockmap";
function createNsisWebDifferentialUpdateInfo(artifactPath, packageFiles) {
if (packageFiles == null) {
return null;
}
const keys = Object.keys(packageFiles);
if (keys.length <= 0) {
return null;
}
const packages = {};
for (const arch of keys) {
const packageFileInfo = packageFiles[arch];
const file = path.basename(packageFileInfo.path);
packages[arch] = {
...packageFileInfo,
path: file,
// https://github.com/electron-userland/electron-builder/issues/2583
file,
};
}
return { packages };
}
exports.createNsisWebDifferentialUpdateInfo = createNsisWebDifferentialUpdateInfo;
function configureDifferentialAwareArchiveOptions(archiveOptions) {
/*
* dict size 64 MB: Full: 33,744.88 KB, To download: 17,630.3 KB (52%)
* dict size 16 MB: Full: 33,936.84 KB, To download: 16,175.9 KB (48%)
* dict size 8 MB: Full: 34,187.59 KB, To download: 8,229.9 KB (24%)
* dict size 4 MB: Full: 34,628.73 KB, To download: 3,782.97 KB (11%)
as we can see, if file changed in one place, all block is invalidated (and update size approximately equals to dict size)
1 MB is used:
1MB:
2018/01/11 11:54:41:0045 File has 59 changed blocks
2018/01/11 11:54:41:0050 Full: 71,588.59 KB, To download: 1,243.39 KB (2%)
4MB:
2018/01/11 11:31:43:0440 Full: 70,303.55 KB, To download: 4,843.27 KB (7%)
2018/01/11 11:31:43:0435 File has 234 changed blocks
*/
archiveOptions.dictSize = 1;
// solid compression leads to a lot of changed blocks
archiveOptions.solid = false;
// do not allow to change compression level to avoid different packages
archiveOptions.compression = "normal";
return archiveOptions;
}
exports.configureDifferentialAwareArchiveOptions = configureDifferentialAwareArchiveOptions;
async function appendBlockmap(file) {
builder_util_1.log.info({ file: builder_util_1.log.filePath(file) }, "building embedded block map");
return await (0, appBuilder_1.executeAppBuilderAsJson)(["blockmap", "--input", file, "--compression", "deflate"]);
}
exports.appendBlockmap = appendBlockmap;
async function createBlockmap(file, target, packager, safeArtifactName) {
const blockMapFile = `${file}${exports.BLOCK_MAP_FILE_SUFFIX}`;
builder_util_1.log.info({ blockMapFile: builder_util_1.log.filePath(blockMapFile) }, "building block map");
const updateInfo = await (0, appBuilder_1.executeAppBuilderAsJson)(["blockmap", "--input", file, "--output", blockMapFile]);
await packager.info.callArtifactBuildCompleted({
file: blockMapFile,
safeArtifactName: safeArtifactName == null ? null : `${safeArtifactName}${exports.BLOCK_MAP_FILE_SUFFIX}`,
target,
arch: null,
packager,
updateInfo,
});
return updateInfo;
}
exports.createBlockmap = createBlockmap;
//# sourceMappingURL=differentialUpdateInfoBuilder.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,9 @@
export type Commands = {
OutFile: string;
VIProductVersion?: string;
VIAddVersionKey: Array<string>;
Unicode: boolean;
Icon?: string;
SetCompress?: "off";
SetCompressor?: "zlib";
};

View File

@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=Commands.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"Commands.js","sourceRoot":"","sources":["../../../src/targets/nsis/Commands.ts"],"names":[],"mappings":"","sourcesContent":["export type Commands = {\n OutFile: string\n VIProductVersion?: string\n VIAddVersionKey: Array<string>\n Unicode: boolean\n Icon?: string\n SetCompress?: \"off\"\n SetCompressor?: \"zlib\"\n}\n"]}

View File

@ -0,0 +1,78 @@
/// <reference types="node" />
import { PortableOptions } from "./nsisOptions";
import { PathLike } from "fs";
/**
* Parameters declared as environment variables in NSIS scripts.
* The documentation vaguely explains "All other electron-builder specific flags (e.g. ONE_CLICK) are still defined."
* Parameters with null values in TypeScript can be treated as Boolean values using "!Ifdef" in NSIS Script.
*/
export type Defines = {
APP_ID: string;
APP_GUID: unknown;
UNINSTALL_APP_KEY: unknown;
PRODUCT_NAME: string;
PRODUCT_FILENAME: string;
APP_FILENAME: string;
APP_DESCRIPTION: string;
VERSION: string;
PROJECT_DIR: string;
BUILD_RESOURCES_DIR: string;
APP_PACKAGE_NAME: string;
ENABLE_LOGGING_ELECTRON_BUILDER?: null;
UNINSTALL_REGISTRY_KEY_2?: string;
MUI_ICON?: unknown;
MUI_UNICON?: unknown;
APP_DIR_64?: string;
APP_DIR_ARM64?: string;
APP_DIR_32?: string;
APP_BUILD_DIR?: string;
APP_64?: string;
APP_ARM64?: string;
APP_32?: string;
APP_64_NAME?: string;
APP_ARM64_NAME?: string;
APP_32_NAME?: string;
APP_64_HASH?: string;
APP_ARM64_HASH?: string;
APP_32_HASH?: string;
APP_64_UNPACKED_SIZE?: string;
APP_ARM64_UNPACKED_SIZE?: string;
APP_32_UNPACKED_SIZE?: string;
REQUEST_EXECUTION_LEVEL?: PortableOptions["requestExecutionLevel"];
UNPACK_DIR_NAME?: string | false;
SPLASH_IMAGE?: unknown;
ESTIMATED_SIZE?: number;
COMPRESS?: "auto";
BUILD_UNINSTALLER?: null;
UNINSTALLER_OUT_FILE?: PathLike;
ONE_CLICK?: null;
RUN_AFTER_FINISH?: null;
HEADER_ICO?: string;
HIDE_RUN_AFTER_FINISH?: null;
MUI_HEADERIMAGE?: null;
MUI_HEADERIMAGE_RIGHT?: null;
MUI_HEADERIMAGE_BITMAP?: string;
MUI_WELCOMEFINISHPAGE_BITMAP?: string;
MUI_UNWELCOMEFINISHPAGE_BITMAP?: string;
MULTIUSER_INSTALLMODE_ALLOW_ELEVATION?: null;
INSTALL_MODE_PER_ALL_USERS?: null;
INSTALL_MODE_PER_ALL_USERS_DEFAULT?: null;
INSTALL_MODE_PER_ALL_USERS_REQUIRED?: null;
allowToChangeInstallationDirectory?: null;
removeDefaultUninstallWelcomePage?: null;
MENU_FILENAME?: string;
SHORTCUT_NAME?: string;
DELETE_APP_DATA_ON_UNINSTALL?: null;
UNINSTALLER_ICON?: string;
UNINSTALL_DISPLAY_NAME?: string;
RECREATE_DESKTOP_SHORTCUT?: null;
DO_NOT_CREATE_DESKTOP_SHORTCUT?: null;
DO_NOT_CREATE_START_MENU_SHORTCUT?: null;
DISPLAY_LANG_SELECTOR?: null;
COMPANY_NAME?: string;
APP_PRODUCT_FILENAME?: string;
APP_PACKAGE_STORE_FILE?: string;
APP_INSTALLER_STORE_FILE?: string;
ZIP_COMPRESSION?: null;
COMPRESSION_METHOD?: "zip" | "7z";
};

View File

@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=Defines.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"Defines.js","sourceRoot":"","sources":["../../../src/targets/nsis/Defines.ts"],"names":[],"mappings":"","sourcesContent":["import { PortableOptions } from \"./nsisOptions\"\nimport { PathLike } from \"fs\"\n/**\n * Parameters declared as environment variables in NSIS scripts.\n * The documentation vaguely explains \"All other electron-builder specific flags (e.g. ONE_CLICK) are still defined.\"\n * Parameters with null values in TypeScript can be treated as Boolean values using \"!Ifdef\" in NSIS Script.\n */\nexport type Defines = {\n APP_ID: string\n APP_GUID: unknown\n UNINSTALL_APP_KEY: unknown\n PRODUCT_NAME: string\n PRODUCT_FILENAME: string\n APP_FILENAME: string\n APP_DESCRIPTION: string\n VERSION: string\n\n PROJECT_DIR: string\n BUILD_RESOURCES_DIR: string\n\n APP_PACKAGE_NAME: string\n\n ENABLE_LOGGING_ELECTRON_BUILDER?: null\n UNINSTALL_REGISTRY_KEY_2?: string\n\n MUI_ICON?: unknown\n MUI_UNICON?: unknown\n\n APP_DIR_64?: string\n APP_DIR_ARM64?: string\n APP_DIR_32?: string\n\n APP_BUILD_DIR?: string\n\n APP_64?: string\n APP_ARM64?: string\n APP_32?: string\n\n APP_64_NAME?: string\n APP_ARM64_NAME?: string\n APP_32_NAME?: string\n\n APP_64_HASH?: string\n APP_ARM64_HASH?: string\n APP_32_HASH?: string\n\n APP_64_UNPACKED_SIZE?: string\n APP_ARM64_UNPACKED_SIZE?: string\n APP_32_UNPACKED_SIZE?: string\n\n REQUEST_EXECUTION_LEVEL?: PortableOptions[\"requestExecutionLevel\"]\n\n UNPACK_DIR_NAME?: string | false\n\n SPLASH_IMAGE?: unknown\n\n ESTIMATED_SIZE?: number\n\n COMPRESS?: \"auto\"\n\n BUILD_UNINSTALLER?: null\n UNINSTALLER_OUT_FILE?: PathLike\n\n ONE_CLICK?: null\n RUN_AFTER_FINISH?: null\n HEADER_ICO?: string\n HIDE_RUN_AFTER_FINISH?: null\n\n MUI_HEADERIMAGE?: null\n MUI_HEADERIMAGE_RIGHT?: null\n MUI_HEADERIMAGE_BITMAP?: string\n\n MUI_WELCOMEFINISHPAGE_BITMAP?: string\n MUI_UNWELCOMEFINISHPAGE_BITMAP?: string\n\n MULTIUSER_INSTALLMODE_ALLOW_ELEVATION?: null\n\n INSTALL_MODE_PER_ALL_USERS?: null\n INSTALL_MODE_PER_ALL_USERS_DEFAULT?: null\n INSTALL_MODE_PER_ALL_USERS_REQUIRED?: null\n\n allowToChangeInstallationDirectory?: null\n\n removeDefaultUninstallWelcomePage?: null\n\n MENU_FILENAME?: string\n\n SHORTCUT_NAME?: string\n\n DELETE_APP_DATA_ON_UNINSTALL?: null\n\n UNINSTALLER_ICON?: string\n UNINSTALL_DISPLAY_NAME?: string\n\n RECREATE_DESKTOP_SHORTCUT?: null\n\n DO_NOT_CREATE_DESKTOP_SHORTCUT?: null\n\n DO_NOT_CREATE_START_MENU_SHORTCUT?: null\n\n DISPLAY_LANG_SELECTOR?: null\n\n COMPANY_NAME?: string\n\n APP_PRODUCT_FILENAME?: string\n\n APP_PACKAGE_STORE_FILE?: string\n\n APP_INSTALLER_STORE_FILE?: string\n\n ZIP_COMPRESSION?: null\n\n COMPRESSION_METHOD?: \"zip\" | \"7z\"\n}\n"]}

View File

@ -0,0 +1,36 @@
import { Arch } from "builder-util";
import { PackageFileInfo } from "builder-util-runtime";
import { Target } from "../../core";
import { WinPackager } from "../../winPackager";
import { Defines } from "./Defines";
import { NsisOptions } from "./nsisOptions";
import { AppPackageHelper } from "./nsisUtil";
export declare class NsisTarget extends Target {
readonly packager: WinPackager;
readonly outDir: string;
protected readonly packageHelper: AppPackageHelper;
readonly options: NsisOptions;
/** @private */
readonly archs: Map<Arch, string>;
readonly isAsyncSupported = false;
constructor(packager: WinPackager, outDir: string, targetName: string, packageHelper: AppPackageHelper);
build(appOutDir: string, arch: Arch): Promise<void>;
get isBuildDifferentialAware(): boolean;
private getPreCompressedFileExtensions;
/** @private */
buildAppPackage(appOutDir: string, arch: Arch): Promise<PackageFileInfo>;
protected get installerFilenamePattern(): string;
private get isPortable();
finishBuild(): Promise<any>;
private buildInstaller;
protected generateGitHubInstallerName(): string;
private get isUnicodeEnabled();
get isWebInstaller(): boolean;
private computeScriptAndSignUninstaller;
private computeVersionKey;
protected configureDefines(oneClick: boolean, defines: Defines): Promise<any>;
private configureDefinesForAllTypeOfInstaller;
private executeMakensis;
private computeCommonInstallerScriptHeader;
private computeFinalScript;
}

View File

@ -0,0 +1,682 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.NsisTarget = void 0;
const bluebird_lst_1 = require("bluebird-lst");
const builder_util_1 = require("builder-util");
const builder_util_runtime_1 = require("builder-util-runtime");
const fs_1 = require("builder-util/out/fs");
const debug_1 = require("debug");
const fs = require("fs");
const fs_extra_1 = require("fs-extra");
const path = require("path");
const binDownload_1 = require("../../binDownload");
const core_1 = require("../../core");
const CommonWindowsInstallerConfiguration_1 = require("../../options/CommonWindowsInstallerConfiguration");
const platformPackager_1 = require("../../platformPackager");
const hash_1 = require("../../util/hash");
const macosVersion_1 = require("../../util/macosVersion");
const timer_1 = require("../../util/timer");
const wine_1 = require("../../wine");
const archive_1 = require("../archive");
const differentialUpdateInfoBuilder_1 = require("../differentialUpdateInfoBuilder");
const targetUtil_1 = require("../targetUtil");
const nsisLang_1 = require("./nsisLang");
const nsisLicense_1 = require("./nsisLicense");
const nsisScriptGenerator_1 = require("./nsisScriptGenerator");
const nsisUtil_1 = require("./nsisUtil");
const debug = (0, debug_1.default)("electron-builder:nsis");
// noinspection SpellCheckingInspection
const ELECTRON_BUILDER_NS_UUID = builder_util_runtime_1.UUID.parse("50e065bc-3134-11e6-9bab-38c9862bdaf3");
// noinspection SpellCheckingInspection
const nsisResourcePathPromise = () => (0, binDownload_1.getBinFromUrl)("nsis-resources", "3.4.1", "Dqd6g+2buwwvoG1Vyf6BHR1b+25QMmPcwZx40atOT57gH27rkjOei1L0JTldxZu4NFoEmW4kJgZ3DlSWVON3+Q==");
const USE_NSIS_BUILT_IN_COMPRESSOR = false;
class NsisTarget extends core_1.Target {
constructor(packager, outDir, targetName, packageHelper) {
super(targetName);
this.packager = packager;
this.outDir = outDir;
this.packageHelper = packageHelper;
/** @private */
this.archs = new Map();
this.isAsyncSupported = false;
this.packageHelper.refCount++;
this.options =
targetName === "portable"
? Object.create(null)
: {
preCompressedFileExtensions: [".avi", ".mov", ".m4v", ".mp4", ".m4p", ".qt", ".mkv", ".webm", ".vmdk"],
...this.packager.config.nsis,
};
if (targetName !== "nsis") {
Object.assign(this.options, this.packager.config[targetName === "nsis-web" ? "nsisWeb" : targetName]);
}
const deps = packager.info.metadata.dependencies;
if (deps != null && deps["electron-squirrel-startup"] != null) {
builder_util_1.log.warn('"electron-squirrel-startup" dependency is not required for NSIS');
}
nsisUtil_1.NsisTargetOptions.resolve(this.options);
}
build(appOutDir, arch) {
this.archs.set(arch, appOutDir);
return Promise.resolve();
}
get isBuildDifferentialAware() {
return !this.isPortable && this.options.differentialPackage !== false;
}
getPreCompressedFileExtensions() {
const result = this.isWebInstaller ? null : this.options.preCompressedFileExtensions;
return result == null ? null : (0, builder_util_1.asArray)(result).map(it => (it.startsWith(".") ? it : `.${it}`));
}
/** @private */
async buildAppPackage(appOutDir, arch) {
const options = this.options;
const packager = this.packager;
const isBuildDifferentialAware = this.isBuildDifferentialAware;
const format = !isBuildDifferentialAware && options.useZip ? "zip" : "7z";
const archiveFile = path.join(this.outDir, `${packager.appInfo.sanitizedName}-${packager.appInfo.version}-${builder_util_1.Arch[arch]}.nsis.${format}`);
const preCompressedFileExtensions = this.getPreCompressedFileExtensions();
const archiveOptions = {
withoutDir: true,
compression: packager.compression,
excluded: preCompressedFileExtensions == null ? null : preCompressedFileExtensions.map(it => `*${it}`),
};
const timer = (0, timer_1.time)(`nsis package, ${builder_util_1.Arch[arch]}`);
await (0, archive_1.archive)(format, archiveFile, appOutDir, isBuildDifferentialAware ? (0, differentialUpdateInfoBuilder_1.configureDifferentialAwareArchiveOptions)(archiveOptions) : archiveOptions);
timer.end();
if (isBuildDifferentialAware && this.isWebInstaller) {
const data = await (0, differentialUpdateInfoBuilder_1.appendBlockmap)(archiveFile);
return {
...data,
path: archiveFile,
};
}
else {
return await createPackageFileInfo(archiveFile);
}
}
get installerFilenamePattern() {
// tslint:disable:no-invalid-template-strings
return "${productName} " + (this.isPortable ? "" : "Setup ") + "${version}.${ext}";
}
get isPortable() {
return this.name === "portable";
}
async finishBuild() {
try {
const { pattern } = this.packager.artifactPatternConfig(this.options, this.installerFilenamePattern);
const builds = new Set([this.archs]);
if (pattern.includes("${arch}") && this.archs.size > 1) {
;
[...this.archs].forEach(([arch, appOutDir]) => builds.add(new Map().set(arch, appOutDir)));
}
const doBuildArchs = builds.values();
for (const archs of doBuildArchs) {
await this.buildInstaller(archs);
}
}
finally {
await this.packageHelper.finishBuild();
}
}
async buildInstaller(archs) {
var _a;
const primaryArch = archs.size === 1 ? archs.keys().next().value : null;
const packager = this.packager;
const appInfo = packager.appInfo;
const options = this.options;
const installerFilename = packager.expandArtifactNamePattern(options, "exe", primaryArch, this.installerFilenamePattern, false, this.packager.platformSpecificBuildOptions.defaultArch);
const oneClick = options.oneClick !== false;
const installerPath = path.join(this.outDir, installerFilename);
const logFields = {
target: this.name,
file: builder_util_1.log.filePath(installerPath),
archs: Array.from(archs.keys())
.map(it => builder_util_1.Arch[it])
.join(", "),
};
const isPerMachine = options.perMachine === true;
if (!this.isPortable) {
logFields.oneClick = oneClick;
logFields.perMachine = isPerMachine;
}
await packager.info.callArtifactBuildStarted({
targetPresentableName: this.name,
file: installerPath,
arch: primaryArch,
}, logFields);
const guid = options.guid || builder_util_runtime_1.UUID.v5(appInfo.id, ELECTRON_BUILDER_NS_UUID);
const uninstallAppKey = guid.replace(/\\/g, " - ");
const defines = {
APP_ID: appInfo.id,
APP_GUID: guid,
// Windows bug - entry in Software\Microsoft\Windows\CurrentVersion\Uninstall cannot have \ symbols (dir)
UNINSTALL_APP_KEY: uninstallAppKey,
PRODUCT_NAME: appInfo.productName,
PRODUCT_FILENAME: appInfo.productFilename,
APP_FILENAME: (0, targetUtil_1.getWindowsInstallationDirName)(appInfo, !oneClick || isPerMachine),
APP_DESCRIPTION: appInfo.description,
VERSION: appInfo.version,
PROJECT_DIR: packager.projectDir,
BUILD_RESOURCES_DIR: packager.info.buildResourcesDir,
APP_PACKAGE_NAME: (0, targetUtil_1.getWindowsInstallationAppPackageName)(appInfo.name),
};
if ((_a = options.customNsisBinary) === null || _a === void 0 ? void 0 : _a.debugLogging) {
defines.ENABLE_LOGGING_ELECTRON_BUILDER = null;
}
if (uninstallAppKey !== guid) {
defines.UNINSTALL_REGISTRY_KEY_2 = `Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\${guid}`;
}
const commands = {
OutFile: `"${installerPath}"`,
VIProductVersion: appInfo.getVersionInWeirdWindowsForm(),
VIAddVersionKey: this.computeVersionKey(),
Unicode: this.isUnicodeEnabled,
};
const isPortable = this.isPortable;
const iconPath = (isPortable ? null : await packager.getResource(options.installerIcon, "installerIcon.ico")) || (await packager.getIconPath());
if (iconPath != null) {
if (isPortable) {
commands.Icon = `"${iconPath}"`;
}
else {
defines.MUI_ICON = iconPath;
defines.MUI_UNICON = iconPath;
}
}
const packageFiles = {};
let estimatedSize = 0;
if (this.isPortable && options.useZip) {
for (const [arch, dir] of archs.entries()) {
defines[arch === builder_util_1.Arch.x64 ? "APP_DIR_64" : arch === builder_util_1.Arch.arm64 ? "APP_DIR_ARM64" : "APP_DIR_32"] = dir;
}
}
else if (USE_NSIS_BUILT_IN_COMPRESSOR && archs.size === 1) {
defines.APP_BUILD_DIR = archs.get(archs.keys().next().value);
}
else {
await bluebird_lst_1.default.map(archs.keys(), async (arch) => {
const { fileInfo, unpackedSize } = await this.packageHelper.packArch(arch, this);
const file = fileInfo.path;
const defineKey = arch === builder_util_1.Arch.x64 ? "APP_64" : arch === builder_util_1.Arch.arm64 ? "APP_ARM64" : "APP_32";
defines[defineKey] = file;
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
const defineNameKey = `${defineKey}_NAME`;
defines[defineNameKey] = path.basename(file);
// nsis expect a hexadecimal string
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
const defineHashKey = `${defineKey}_HASH`;
defines[defineHashKey] = Buffer.from(fileInfo.sha512, "base64").toString("hex").toUpperCase();
// NSIS accepts size in KiloBytes and supports only whole numbers
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
const defineUnpackedSizeKey = `${defineKey}_UNPACKED_SIZE`;
defines[defineUnpackedSizeKey] = Math.ceil(unpackedSize / 1024).toString();
if (this.isWebInstaller) {
await packager.dispatchArtifactCreated(file, this, arch);
packageFiles[builder_util_1.Arch[arch]] = fileInfo;
}
const path7za = await (0, builder_util_1.getPath7za)();
const archiveInfo = (await (0, builder_util_1.exec)(path7za, ["l", file])).trim();
// after adding blockmap data will be "Warnings: 1" in the end of output
const match = /(\d+)\s+\d+\s+\d+\s+files/.exec(archiveInfo);
if (match == null) {
builder_util_1.log.warn({ output: archiveInfo }, "cannot compute size of app package");
}
else {
estimatedSize += parseInt(match[1], 10);
}
});
}
this.configureDefinesForAllTypeOfInstaller(defines);
if (isPortable) {
const { unpackDirName, requestExecutionLevel, splashImage } = options;
defines.REQUEST_EXECUTION_LEVEL = requestExecutionLevel || "user";
// https://github.com/electron-userland/electron-builder/issues/5764
if (typeof unpackDirName === "string" || !unpackDirName) {
defines.UNPACK_DIR_NAME = unpackDirName || (await (0, builder_util_1.executeAppBuilder)(["ksuid"]));
}
if (splashImage != null) {
defines.SPLASH_IMAGE = path.resolve(packager.projectDir, splashImage);
}
}
else {
await this.configureDefines(oneClick, defines);
}
if (estimatedSize !== 0) {
// in kb
defines.ESTIMATED_SIZE = Math.round(estimatedSize / 1024);
}
if (packager.compression === "store") {
commands.SetCompress = "off";
}
else {
// difference - 33.540 vs 33.601, only 61 KB (but zip is faster to decompress)
// do not use /SOLID - "With solid compression, files are uncompressed to temporary file before they are copied to their final destination",
// it is not good for portable installer (where built-in NSIS compression is used). http://forums.winamp.com/showpost.php?p=2982902&postcount=6
commands.SetCompressor = "zlib";
if (!this.isWebInstaller) {
defines.COMPRESS = "auto";
}
}
debug(defines);
debug(commands);
if (packager.packagerOptions.effectiveOptionComputed != null && (await packager.packagerOptions.effectiveOptionComputed([defines, commands]))) {
return;
}
// prepare short-version variants of defines and commands, to make an uninstaller that doesn't differ much from the previous one
const definesUninstaller = { ...defines };
const commandsUninstaller = { ...commands };
if (appInfo.shortVersion != null) {
definesUninstaller.VERSION = appInfo.shortVersion;
commandsUninstaller.VIProductVersion = appInfo.shortVersionWindows;
commandsUninstaller.VIAddVersionKey = this.computeVersionKey(true);
}
const sharedHeader = await this.computeCommonInstallerScriptHeader();
const script = isPortable
? await (0, fs_extra_1.readFile)(path.join(nsisUtil_1.nsisTemplatesDir, "portable.nsi"), "utf8")
: await this.computeScriptAndSignUninstaller(definesUninstaller, commandsUninstaller, installerPath, sharedHeader, archs);
// copy outfile name into main options, as the computeScriptAndSignUninstaller function was kind enough to add important data to temporary defines.
defines.UNINSTALLER_OUT_FILE = definesUninstaller.UNINSTALLER_OUT_FILE;
await this.executeMakensis(defines, commands, sharedHeader + (await this.computeFinalScript(script, true, archs)));
await Promise.all([packager.sign(installerPath), defines.UNINSTALLER_OUT_FILE == null ? Promise.resolve() : (0, fs_extra_1.unlink)(defines.UNINSTALLER_OUT_FILE)]);
const safeArtifactName = (0, platformPackager_1.computeSafeArtifactNameIfNeeded)(installerFilename, () => this.generateGitHubInstallerName());
let updateInfo;
if (this.isWebInstaller) {
updateInfo = (0, differentialUpdateInfoBuilder_1.createNsisWebDifferentialUpdateInfo)(installerPath, packageFiles);
}
else if (this.isBuildDifferentialAware) {
updateInfo = await (0, differentialUpdateInfoBuilder_1.createBlockmap)(installerPath, this, packager, safeArtifactName);
}
if (updateInfo != null && isPerMachine && (oneClick || options.packElevateHelper)) {
updateInfo.isAdminRightsRequired = true;
}
await packager.info.callArtifactBuildCompleted({
file: installerPath,
updateInfo,
target: this,
packager,
arch: primaryArch,
safeArtifactName,
isWriteUpdateInfo: !this.isPortable,
});
}
generateGitHubInstallerName() {
const appInfo = this.packager.appInfo;
const classifier = appInfo.name.toLowerCase() === appInfo.name ? "setup-" : "Setup-";
return `${appInfo.name}-${this.isPortable ? "" : classifier}${appInfo.version}.exe`;
}
get isUnicodeEnabled() {
return this.options.unicode !== false;
}
get isWebInstaller() {
return false;
}
async computeScriptAndSignUninstaller(defines, commands, installerPath, sharedHeader, archs) {
const packager = this.packager;
const customScriptPath = await packager.getResource(this.options.script, "installer.nsi");
const script = await (0, fs_extra_1.readFile)(customScriptPath || path.join(nsisUtil_1.nsisTemplatesDir, "installer.nsi"), "utf8");
if (customScriptPath != null) {
builder_util_1.log.info({ reason: "custom NSIS script is used" }, "uninstaller is not signed by electron-builder");
return script;
}
// https://github.com/electron-userland/electron-builder/issues/2103
// it is more safe and reliable to write uninstaller to our out dir
const uninstallerPath = path.join(this.outDir, `__uninstaller-${this.name}-${this.packager.appInfo.sanitizedName}.exe`);
const isWin = process.platform === "win32";
defines.BUILD_UNINSTALLER = null;
defines.UNINSTALLER_OUT_FILE = isWin ? uninstallerPath : path.win32.join("Z:", uninstallerPath);
await this.executeMakensis(defines, commands, sharedHeader + (await this.computeFinalScript(script, false, archs)));
// http://forums.winamp.com/showthread.php?p=3078545
if ((0, macosVersion_1.isMacOsCatalina)()) {
try {
await nsisUtil_1.UninstallerReader.exec(installerPath, uninstallerPath);
}
catch (error) {
builder_util_1.log.warn(`packager.vm is used: ${error.message}`);
const vm = await packager.vm.value;
await vm.exec(installerPath, []);
// Parallels VM can exit after command execution, but NSIS continue to be running
let i = 0;
while (!(await (0, fs_1.exists)(uninstallerPath)) && i++ < 100) {
// noinspection JSUnusedLocalSymbols
// eslint-disable-next-line @typescript-eslint/no-unused-vars
await new Promise((resolve, _reject) => setTimeout(resolve, 300));
}
}
}
else {
await (0, wine_1.execWine)(installerPath, null, [], { env: { __COMPAT_LAYER: "RunAsInvoker" } });
}
await packager.sign(uninstallerPath, "signing NSIS uninstaller");
delete defines.BUILD_UNINSTALLER;
// platform-specific path, not wine
defines.UNINSTALLER_OUT_FILE = uninstallerPath;
return script;
}
computeVersionKey(short = false) {
// Error: invalid VIProductVersion format, should be X.X.X.X
// so, we must strip beta
const localeId = this.options.language || "1033";
const appInfo = this.packager.appInfo;
const versionKey = [
`/LANG=${localeId} ProductName "${appInfo.productName}"`,
`/LANG=${localeId} ProductVersion "${appInfo.version}"`,
`/LANG=${localeId} LegalCopyright "${appInfo.copyright}"`,
`/LANG=${localeId} FileDescription "${appInfo.description}"`,
`/LANG=${localeId} FileVersion "${appInfo.buildVersion}"`,
];
if (short) {
versionKey[1] = `/LANG=${localeId} ProductVersion "${appInfo.shortVersion}"`;
versionKey[4] = `/LANG=${localeId} FileVersion "${appInfo.shortVersion}"`;
}
(0, builder_util_1.use)(this.packager.platformSpecificBuildOptions.legalTrademarks, it => versionKey.push(`/LANG=${localeId} LegalTrademarks "${it}"`));
(0, builder_util_1.use)(appInfo.companyName, it => versionKey.push(`/LANG=${localeId} CompanyName "${it}"`));
return versionKey;
}
configureDefines(oneClick, defines) {
const packager = this.packager;
const options = this.options;
const asyncTaskManager = new builder_util_1.AsyncTaskManager(packager.info.cancellationToken);
if (oneClick) {
defines.ONE_CLICK = null;
if (options.runAfterFinish !== false) {
defines.RUN_AFTER_FINISH = null;
}
asyncTaskManager.add(async () => {
const installerHeaderIcon = await packager.getResource(options.installerHeaderIcon, "installerHeaderIcon.ico");
if (installerHeaderIcon != null) {
defines.HEADER_ICO = installerHeaderIcon;
}
});
}
else {
if (options.runAfterFinish === false) {
defines.HIDE_RUN_AFTER_FINISH = null;
}
asyncTaskManager.add(async () => {
const installerHeader = await packager.getResource(options.installerHeader, "installerHeader.bmp");
if (installerHeader != null) {
defines.MUI_HEADERIMAGE = null;
defines.MUI_HEADERIMAGE_RIGHT = null;
defines.MUI_HEADERIMAGE_BITMAP = installerHeader;
}
});
asyncTaskManager.add(async () => {
const bitmap = (await packager.getResource(options.installerSidebar, "installerSidebar.bmp")) || "${NSISDIR}\\Contrib\\Graphics\\Wizard\\nsis3-metro.bmp";
defines.MUI_WELCOMEFINISHPAGE_BITMAP = bitmap;
defines.MUI_UNWELCOMEFINISHPAGE_BITMAP = (await packager.getResource(options.uninstallerSidebar, "uninstallerSidebar.bmp")) || bitmap;
});
if (options.allowElevation !== false) {
defines.MULTIUSER_INSTALLMODE_ALLOW_ELEVATION = null;
}
}
if (options.perMachine === true) {
defines.INSTALL_MODE_PER_ALL_USERS = null;
}
if (options.selectPerMachineByDefault === true) {
defines.INSTALL_MODE_PER_ALL_USERS_DEFAULT = null;
}
if (!oneClick || options.perMachine === true) {
defines.INSTALL_MODE_PER_ALL_USERS_REQUIRED = null;
}
if (options.allowToChangeInstallationDirectory) {
if (oneClick) {
throw new builder_util_1.InvalidConfigurationError("allowToChangeInstallationDirectory makes sense only for assisted installer (please set oneClick to false)");
}
defines.allowToChangeInstallationDirectory = null;
}
if (options.removeDefaultUninstallWelcomePage) {
defines.removeDefaultUninstallWelcomePage = null;
}
const commonOptions = (0, CommonWindowsInstallerConfiguration_1.getEffectiveOptions)(options, packager);
if (commonOptions.menuCategory != null) {
defines.MENU_FILENAME = commonOptions.menuCategory;
}
defines.SHORTCUT_NAME = commonOptions.shortcutName;
if (options.deleteAppDataOnUninstall) {
defines.DELETE_APP_DATA_ON_UNINSTALL = null;
}
asyncTaskManager.add(async () => {
const uninstallerIcon = await packager.getResource(options.uninstallerIcon, "uninstallerIcon.ico");
if (uninstallerIcon != null) {
// we don't need to copy MUI_UNICON (defaults to app icon), so, we have 2 defines
defines.UNINSTALLER_ICON = uninstallerIcon;
defines.MUI_UNICON = uninstallerIcon;
}
});
defines.UNINSTALL_DISPLAY_NAME = packager.expandMacro(options.uninstallDisplayName || "${productName} ${version}", null, {}, false);
if (commonOptions.isCreateDesktopShortcut === CommonWindowsInstallerConfiguration_1.DesktopShortcutCreationPolicy.NEVER) {
defines.DO_NOT_CREATE_DESKTOP_SHORTCUT = null;
}
if (commonOptions.isCreateDesktopShortcut === CommonWindowsInstallerConfiguration_1.DesktopShortcutCreationPolicy.ALWAYS) {
defines.RECREATE_DESKTOP_SHORTCUT = null;
}
if (!commonOptions.isCreateStartMenuShortcut) {
defines.DO_NOT_CREATE_START_MENU_SHORTCUT = null;
}
if (options.displayLanguageSelector === true) {
defines.DISPLAY_LANG_SELECTOR = null;
}
return asyncTaskManager.awaitTasks();
}
configureDefinesForAllTypeOfInstaller(defines) {
const appInfo = this.packager.appInfo;
const companyName = appInfo.companyName;
if (companyName != null) {
defines.COMPANY_NAME = companyName;
}
// electron uses product file name as app data, define it as well to remove on uninstall
if (defines.APP_FILENAME !== appInfo.productFilename) {
defines.APP_PRODUCT_FILENAME = appInfo.productFilename;
}
if (this.isWebInstaller) {
defines.APP_PACKAGE_STORE_FILE = `${appInfo.updaterCacheDirName}\\${builder_util_runtime_1.CURRENT_APP_PACKAGE_FILE_NAME}`;
}
else {
defines.APP_INSTALLER_STORE_FILE = `${appInfo.updaterCacheDirName}\\${builder_util_runtime_1.CURRENT_APP_INSTALLER_FILE_NAME}`;
}
if (!this.isWebInstaller && defines.APP_BUILD_DIR == null) {
const options = this.options;
if (options.useZip) {
defines.ZIP_COMPRESSION = null;
}
defines.COMPRESSION_METHOD = options.useZip ? "zip" : "7z";
}
}
async executeMakensis(defines, commands, script) {
const args = this.options.warningsAsErrors === false ? [] : ["-WX"];
args.push("-INPUTCHARSET", "UTF8");
for (const name of Object.keys(defines)) {
const value = defines[name];
if (value == null) {
args.push(`-D${name}`);
}
else {
args.push(`-D${name}=${value}`);
}
}
for (const name of Object.keys(commands)) {
const value = commands[name];
if (Array.isArray(value)) {
for (const c of value) {
args.push(`-X${name} ${c}`);
}
}
else {
args.push(`-X${name} ${value}`);
}
}
args.push("-");
if (this.packager.debugLogger.isEnabled) {
this.packager.debugLogger.add("nsis.script", script);
}
const nsisPath = await (0, nsisUtil_1.NSIS_PATH)();
const command = path.join(nsisPath, process.platform === "darwin" ? "mac" : process.platform === "win32" ? "Bin" : "linux", process.platform === "win32" ? "makensis.exe" : "makensis");
// if (process.platform === "win32") {
// fix for an issue caused by virus scanners, locking the file during write
// https://github.com/electron-userland/electron-builder/issues/5005
await ensureNotBusy(commands["OutFile"].replace(/"/g, ""));
// }
await (0, builder_util_1.spawnAndWrite)(command, args, script, {
// we use NSIS_CONFIG_CONST_DATA_PATH=no to build makensis on Linux, but in any case it doesn't use stubs as MacOS/Windows version, so, we explicitly set NSISDIR
env: { ...process.env, NSISDIR: nsisPath },
cwd: nsisUtil_1.nsisTemplatesDir,
});
}
async computeCommonInstallerScriptHeader() {
const packager = this.packager;
const options = this.options;
const scriptGenerator = new nsisScriptGenerator_1.NsisScriptGenerator();
const langConfigurator = new nsisLang_1.LangConfigurator(options);
scriptGenerator.include(path.join(nsisUtil_1.nsisTemplatesDir, "include", "StdUtils.nsh"));
const includeDir = path.join(nsisUtil_1.nsisTemplatesDir, "include");
scriptGenerator.addIncludeDir(includeDir);
scriptGenerator.flags(["updated", "force-run", "keep-shortcuts", "no-desktop-shortcut", "delete-app-data", "allusers", "currentuser"]);
(0, nsisLang_1.createAddLangsMacro)(scriptGenerator, langConfigurator);
const taskManager = new builder_util_1.AsyncTaskManager(packager.info.cancellationToken);
const pluginArch = this.isUnicodeEnabled ? "x86-unicode" : "x86-ansi";
taskManager.add(async () => {
scriptGenerator.addPluginDir(pluginArch, path.join(await nsisResourcePathPromise(), "plugins", pluginArch));
});
taskManager.add(async () => {
const userPluginDir = path.join(packager.info.buildResourcesDir, pluginArch);
const stat = await (0, fs_1.statOrNull)(userPluginDir);
if (stat != null && stat.isDirectory()) {
scriptGenerator.addPluginDir(pluginArch, userPluginDir);
}
});
taskManager.addTask((0, nsisLang_1.addCustomMessageFileInclude)("messages.yml", packager, scriptGenerator, langConfigurator));
if (!this.isPortable) {
if (options.oneClick === false) {
taskManager.addTask((0, nsisLang_1.addCustomMessageFileInclude)("assistedMessages.yml", packager, scriptGenerator, langConfigurator));
}
taskManager.add(async () => {
const customInclude = await packager.getResource(this.options.include, "installer.nsh");
if (customInclude != null) {
scriptGenerator.addIncludeDir(packager.info.buildResourcesDir);
scriptGenerator.include(customInclude);
}
});
}
await taskManager.awaitTasks();
return scriptGenerator.build();
}
async computeFinalScript(originalScript, isInstaller, archs) {
const packager = this.packager;
const options = this.options;
const langConfigurator = new nsisLang_1.LangConfigurator(options);
const scriptGenerator = new nsisScriptGenerator_1.NsisScriptGenerator();
const taskManager = new builder_util_1.AsyncTaskManager(packager.info.cancellationToken);
if (isInstaller) {
// http://stackoverflow.com/questions/997456/nsis-license-file-based-on-language-selection
taskManager.add(() => (0, nsisLicense_1.computeLicensePage)(packager, options, scriptGenerator, langConfigurator.langs));
}
await taskManager.awaitTasks();
if (this.isPortable) {
return scriptGenerator.build() + originalScript;
}
const preCompressedFileExtensions = this.getPreCompressedFileExtensions();
if (preCompressedFileExtensions != null && preCompressedFileExtensions.length !== 0) {
for (const [arch, dir] of archs.entries()) {
await generateForPreCompressed(preCompressedFileExtensions, dir, arch, scriptGenerator);
}
}
const fileAssociations = packager.fileAssociations;
if (fileAssociations.length !== 0) {
scriptGenerator.include(path.join(path.join(nsisUtil_1.nsisTemplatesDir, "include"), "FileAssociation.nsh"));
if (isInstaller) {
const registerFileAssociationsScript = new nsisScriptGenerator_1.NsisScriptGenerator();
for (const item of fileAssociations) {
const extensions = (0, builder_util_1.asArray)(item.ext).map(platformPackager_1.normalizeExt);
for (const ext of extensions) {
const customIcon = await packager.getResource((0, builder_util_1.getPlatformIconFileName)(item.icon, false), `${extensions[0]}.ico`);
let installedIconPath = "$appExe,0";
if (customIcon != null) {
installedIconPath = `$INSTDIR\\resources\\${path.basename(customIcon)}`;
registerFileAssociationsScript.file(installedIconPath, customIcon);
}
const icon = `"${installedIconPath}"`;
const commandText = `"Open with ${packager.appInfo.productName}"`;
const command = '"$appExe $\\"%1$\\""';
registerFileAssociationsScript.insertMacro("APP_ASSOCIATE", `"${ext}" "${item.name || ext}" "${item.description || ""}" ${icon} ${commandText} ${command}`);
}
}
scriptGenerator.macro("registerFileAssociations", registerFileAssociationsScript);
}
else {
const unregisterFileAssociationsScript = new nsisScriptGenerator_1.NsisScriptGenerator();
for (const item of fileAssociations) {
for (const ext of (0, builder_util_1.asArray)(item.ext)) {
unregisterFileAssociationsScript.insertMacro("APP_UNASSOCIATE", `"${(0, platformPackager_1.normalizeExt)(ext)}" "${item.name || ext}"`);
}
}
scriptGenerator.macro("unregisterFileAssociations", unregisterFileAssociationsScript);
}
}
return scriptGenerator.build() + originalScript;
}
}
exports.NsisTarget = NsisTarget;
async function generateForPreCompressed(preCompressedFileExtensions, dir, arch, scriptGenerator) {
const resourcesDir = path.join(dir, "resources");
const dirInfo = await (0, fs_1.statOrNull)(resourcesDir);
if (dirInfo == null || !dirInfo.isDirectory()) {
return;
}
const nodeModules = `${path.sep}node_modules`;
const preCompressedAssets = await (0, fs_1.walk)(resourcesDir, (file, stat) => {
if (stat.isDirectory()) {
return !file.endsWith(nodeModules);
}
else {
return preCompressedFileExtensions.some(it => file.endsWith(it));
}
});
if (preCompressedAssets.length !== 0) {
const macro = new nsisScriptGenerator_1.NsisScriptGenerator();
for (const file of preCompressedAssets) {
macro.file(`$INSTDIR\\${path.relative(dir, file).replace(/\//g, "\\")}`, file);
}
scriptGenerator.macro(`customFiles_${builder_util_1.Arch[arch]}`, macro);
}
}
async function ensureNotBusy(outFile) {
function isBusy(wasBusyBefore) {
return new Promise((resolve, reject) => {
fs.open(outFile, "r+", (error, fd) => {
try {
if (error != null && error.code === "EBUSY") {
if (!wasBusyBefore) {
builder_util_1.log.info({}, "output file is locked for writing (maybe by virus scanner) => waiting for unlock...");
}
resolve(false);
}
else if (fd == null) {
resolve(true);
}
else {
fs.close(fd, () => resolve(true));
}
}
catch (error) {
reject(error);
}
});
}).then(result => {
if (result) {
return true;
}
else {
return new Promise(resolve => setTimeout(resolve, 2000)).then(() => isBusy(true));
}
});
}
await isBusy(false);
}
async function createPackageFileInfo(file) {
return {
path: file,
size: (await (0, fs_extra_1.stat)(file)).size,
sha512: await (0, hash_1.hashFile)(file),
};
}
//# sourceMappingURL=NsisTarget.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,11 @@
import { WinPackager } from "../../winPackager";
import { NsisTarget } from "./NsisTarget";
import { AppPackageHelper } from "./nsisUtil";
/** @private */
export declare class WebInstallerTarget extends NsisTarget {
constructor(packager: WinPackager, outDir: string, targetName: string, packageHelper: AppPackageHelper);
get isWebInstaller(): boolean;
protected configureDefines(oneClick: boolean, defines: any): Promise<any>;
protected get installerFilenamePattern(): string;
protected generateGitHubInstallerName(): string;
}

View File

@ -0,0 +1,41 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.WebInstallerTarget = void 0;
const PublishManager_1 = require("../../publish/PublishManager");
const NsisTarget_1 = require("./NsisTarget");
/** @private */
class WebInstallerTarget extends NsisTarget_1.NsisTarget {
constructor(packager, outDir, targetName, packageHelper) {
super(packager, outDir, targetName, packageHelper);
}
get isWebInstaller() {
return true;
}
async configureDefines(oneClick, defines) {
//noinspection ES6MissingAwait
await NsisTarget_1.NsisTarget.prototype.configureDefines.call(this, oneClick, defines);
const packager = this.packager;
const options = this.options;
let appPackageUrl = options.appPackageUrl;
if (appPackageUrl == null) {
const publishConfigs = await (0, PublishManager_1.getPublishConfigsForUpdateInfo)(packager, await (0, PublishManager_1.getPublishConfigs)(packager, packager.info.config, null, false), null);
if (publishConfigs == null || publishConfigs.length === 0) {
throw new Error("Cannot compute app package download URL");
}
appPackageUrl = (0, PublishManager_1.computeDownloadUrl)(publishConfigs[0], null, packager);
}
defines.APP_PACKAGE_URL_IS_INCOMPLETE = null;
defines.APP_PACKAGE_URL = appPackageUrl;
}
get installerFilenamePattern() {
// tslint:disable:no-invalid-template-strings
return "${productName} Web Setup ${version}.${ext}";
}
generateGitHubInstallerName() {
const appInfo = this.packager.appInfo;
const classifier = appInfo.name.toLowerCase() === appInfo.name ? "web-setup" : "WebSetup";
return `${appInfo.name}-${classifier}-${appInfo.version}.exe`;
}
}
exports.WebInstallerTarget = WebInstallerTarget;
//# sourceMappingURL=WebInstallerTarget.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"WebInstallerTarget.js","sourceRoot":"","sources":["../../../src/targets/nsis/WebInstallerTarget.ts"],"names":[],"mappings":";;;AAAA,iEAAoH;AAGpH,6CAAyC;AAGzC,eAAe;AACf,MAAa,kBAAmB,SAAQ,uBAAU;IAChD,YAAY,QAAqB,EAAE,MAAc,EAAE,UAAkB,EAAE,aAA+B;QACpG,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,aAAa,CAAC,CAAA;IACpD,CAAC;IAED,IAAI,cAAc;QAChB,OAAO,IAAI,CAAA;IACb,CAAC;IAES,KAAK,CAAC,gBAAgB,CAAC,QAAiB,EAAE,OAAY;QAC9D,8BAA8B;QAC9B,MAAO,uBAAU,CAAC,SAAgC,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAA;QAEjG,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAA;QAC9B,MAAM,OAAO,GAAG,IAAI,CAAC,OAAyB,CAAA;QAE9C,IAAI,aAAa,GAAG,OAAO,CAAC,aAAa,CAAA;QACzC,IAAI,aAAa,IAAI,IAAI,EAAE,CAAC;YAC1B,MAAM,cAAc,GAAG,MAAM,IAAA,+CAA8B,EAAC,QAAQ,EAAE,MAAM,IAAA,kCAAiB,EAAC,QAAQ,EAAE,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,EAAE,IAAI,CAAC,CAAA;YACjJ,IAAI,cAAc,IAAI,IAAI,IAAI,cAAc,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC1D,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAA;YAC5D,CAAC;YAED,aAAa,GAAG,IAAA,mCAAkB,EAAC,cAAc,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAA;QACvE,CAAC;QAED,OAAO,CAAC,6BAA6B,GAAG,IAAI,CAAA;QAC5C,OAAO,CAAC,eAAe,GAAG,aAAa,CAAA;IACzC,CAAC;IAED,IAAc,wBAAwB;QACpC,6CAA6C;QAC7C,OAAO,4CAA4C,CAAA;IACrD,CAAC;IAES,2BAA2B;QACnC,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAA;QACrC,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,UAAU,CAAA;QACzF,OAAO,GAAG,OAAO,CAAC,IAAI,IAAI,UAAU,IAAI,OAAO,CAAC,OAAO,MAAM,CAAA;IAC/D,CAAC;CACF;AAxCD,gDAwCC","sourcesContent":["import { computeDownloadUrl, getPublishConfigs, getPublishConfigsForUpdateInfo } from \"../../publish/PublishManager\"\nimport { WinPackager } from \"../../winPackager\"\nimport { NsisWebOptions } from \"./nsisOptions\"\nimport { NsisTarget } from \"./NsisTarget\"\nimport { AppPackageHelper } from \"./nsisUtil\"\n\n/** @private */\nexport class WebInstallerTarget extends NsisTarget {\n constructor(packager: WinPackager, outDir: string, targetName: string, packageHelper: AppPackageHelper) {\n super(packager, outDir, targetName, packageHelper)\n }\n\n get isWebInstaller(): boolean {\n return true\n }\n\n protected async configureDefines(oneClick: boolean, defines: any): Promise<any> {\n //noinspection ES6MissingAwait\n await (NsisTarget.prototype as WebInstallerTarget).configureDefines.call(this, oneClick, defines)\n\n const packager = this.packager\n const options = this.options as NsisWebOptions\n\n let appPackageUrl = options.appPackageUrl\n if (appPackageUrl == null) {\n const publishConfigs = await getPublishConfigsForUpdateInfo(packager, await getPublishConfigs(packager, packager.info.config, null, false), null)\n if (publishConfigs == null || publishConfigs.length === 0) {\n throw new Error(\"Cannot compute app package download URL\")\n }\n\n appPackageUrl = computeDownloadUrl(publishConfigs[0], null, packager)\n }\n\n defines.APP_PACKAGE_URL_IS_INCOMPLETE = null\n defines.APP_PACKAGE_URL = appPackageUrl\n }\n\n protected get installerFilenamePattern(): string {\n // tslint:disable:no-invalid-template-strings\n return \"${productName} Web Setup ${version}.${ext}\"\n }\n\n protected generateGitHubInstallerName(): string {\n const appInfo = this.packager.appInfo\n const classifier = appInfo.name.toLowerCase() === appInfo.name ? \"web-setup\" : \"WebSetup\"\n return `${appInfo.name}-${classifier}-${appInfo.version}.exe`\n }\n}\n"]}

View File

@ -0,0 +1,10 @@
import { PlatformPackager } from "../../platformPackager";
import { NsisOptions } from "./nsisOptions";
import { NsisScriptGenerator } from "./nsisScriptGenerator";
export declare class LangConfigurator {
readonly isMultiLang: boolean;
readonly langs: Array<string>;
constructor(options: NsisOptions);
}
export declare function createAddLangsMacro(scriptGenerator: NsisScriptGenerator, langConfigurator: LangConfigurator): void;
export declare function addCustomMessageFileInclude(input: string, packager: PlatformPackager<any>, scriptGenerator: NsisScriptGenerator, langConfigurator: LangConfigurator): Promise<void>;

View File

@ -0,0 +1,100 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.addCustomMessageFileInclude = exports.createAddLangsMacro = exports.LangConfigurator = void 0;
const builder_util_1 = require("builder-util");
const langs_1 = require("../../util/langs");
const debug_1 = require("debug");
const fs_extra_1 = require("fs-extra");
const js_yaml_1 = require("js-yaml");
const path = require("path");
const nsisUtil_1 = require("./nsisUtil");
const debug = (0, debug_1.default)("electron-builder:nsis");
class LangConfigurator {
constructor(options) {
const rawList = options.installerLanguages;
if (options.unicode === false || rawList === null || (Array.isArray(rawList) && rawList.length === 0)) {
this.isMultiLang = false;
}
else {
this.isMultiLang = options.multiLanguageInstaller !== false;
}
if (this.isMultiLang) {
this.langs = rawList == null ? langs_1.bundledLanguages.slice() : (0, builder_util_1.asArray)(rawList).map(it => (0, langs_1.toLangWithRegion)(it.replace("-", "_")));
}
else {
this.langs = ["en_US"];
}
}
}
exports.LangConfigurator = LangConfigurator;
function createAddLangsMacro(scriptGenerator, langConfigurator) {
const result = [];
for (const langWithRegion of langConfigurator.langs) {
let name;
if (langWithRegion === "zh_CN") {
name = "SimpChinese";
}
else if (langWithRegion === "zh_TW") {
name = "TradChinese";
}
else if (langWithRegion === "nb_NO") {
name = "Norwegian";
}
else if (langWithRegion === "pt_BR") {
name = "PortugueseBR";
}
else {
const lang = langWithRegion.substring(0, langWithRegion.indexOf("_"));
name = langs_1.langIdToName[lang];
if (name == null) {
throw new Error(`Language name is unknown for ${lang}`);
}
if (name === "Spanish") {
name = "SpanishInternational";
}
}
result.push(`!insertmacro MUI_LANGUAGE "${name}"`);
}
scriptGenerator.macro("addLangs", result);
}
exports.createAddLangsMacro = createAddLangsMacro;
async function writeCustomLangFile(data, packager) {
const file = await packager.getTempFile("messages.nsh");
await (0, fs_extra_1.outputFile)(file, data);
return file;
}
async function addCustomMessageFileInclude(input, packager, scriptGenerator, langConfigurator) {
const data = (0, js_yaml_1.load)(await (0, fs_extra_1.readFile)(path.join(nsisUtil_1.nsisTemplatesDir, input), "utf-8"));
const instructions = computeCustomMessageTranslations(data, langConfigurator).join("\n");
debug(instructions);
scriptGenerator.include(await writeCustomLangFile(instructions, packager));
}
exports.addCustomMessageFileInclude = addCustomMessageFileInclude;
function computeCustomMessageTranslations(messages, langConfigurator) {
const result = [];
const includedLangs = new Set(langConfigurator.langs);
for (const messageId of Object.keys(messages)) {
const langToTranslations = messages[messageId];
const unspecifiedLangs = new Set(langConfigurator.langs);
for (const lang of Object.keys(langToTranslations)) {
const langWithRegion = (0, langs_1.toLangWithRegion)(lang);
if (!includedLangs.has(langWithRegion)) {
continue;
}
const value = langToTranslations[lang];
if (value == null) {
throw new Error(`${messageId} not specified for ${lang}`);
}
result.push(`LangString ${messageId} ${langs_1.lcid[langWithRegion]} "${value.replace(/\n/g, "$\\r$\\n")}"`);
unspecifiedLangs.delete(langWithRegion);
}
if (langConfigurator.isMultiLang) {
const defaultTranslation = langToTranslations.en.replace(/\n/g, "$\\r$\\n");
for (const langWithRegion of unspecifiedLangs) {
result.push(`LangString ${messageId} ${langs_1.lcid[langWithRegion]} "${defaultTranslation}"`);
}
}
}
return result;
}
//# sourceMappingURL=nsisLang.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,4 @@
import { WinPackager } from "../../winPackager";
import { NsisOptions } from "./nsisOptions";
import { NsisScriptGenerator } from "./nsisScriptGenerator";
export declare function computeLicensePage(packager: WinPackager, options: NsisOptions, scriptGenerator: NsisScriptGenerator, languages: Array<string>): Promise<void>;

View File

@ -0,0 +1,53 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.computeLicensePage = void 0;
const langs_1 = require("../../util/langs");
const license_1 = require("../../util/license");
const path = require("path");
const nsisUtil_1 = require("./nsisUtil");
async function computeLicensePage(packager, options, scriptGenerator, languages) {
const license = await (0, license_1.getNotLocalizedLicenseFile)(options.license, packager);
if (license != null) {
let licensePage;
if (license.endsWith(".html")) {
licensePage = [
"!define MUI_PAGE_CUSTOMFUNCTION_SHOW LicenseShow",
"Function LicenseShow",
" FindWindow $R0 `#32770` `` $HWNDPARENT",
" GetDlgItem $R0 $R0 1000",
"EmbedHTML::Load /replace $R0 file://$PLUGINSDIR\\license.html",
"FunctionEnd",
`!insertmacro MUI_PAGE_LICENSE "${path.join(nsisUtil_1.nsisTemplatesDir, "empty-license.txt")}"`,
];
}
else {
licensePage = [`!insertmacro MUI_PAGE_LICENSE "${license}"`];
}
scriptGenerator.macro("licensePage", licensePage);
if (license.endsWith(".html")) {
scriptGenerator.macro("addLicenseFiles", [`File /oname=$PLUGINSDIR\\license.html "${license}"`]);
}
return;
}
const licenseFiles = await (0, license_1.getLicenseFiles)(packager);
if (licenseFiles.length === 0) {
return;
}
const licensePage = [];
const unspecifiedLangs = new Set(languages);
let defaultFile = null;
for (const item of licenseFiles) {
unspecifiedLangs.delete(item.langWithRegion);
if (defaultFile == null) {
defaultFile = item.file;
}
licensePage.push(`LicenseLangString MUILicense ${langs_1.lcid[item.langWithRegion] || item.lang} "${item.file}"`);
}
for (const l of unspecifiedLangs) {
licensePage.push(`LicenseLangString MUILicense ${langs_1.lcid[l]} "${defaultFile}"`);
}
licensePage.push('!insertmacro MUI_PAGE_LICENSE "$(MUILicense)"');
scriptGenerator.macro("licensePage", licensePage);
}
exports.computeLicensePage = computeLicensePage;
//# sourceMappingURL=nsisLicense.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"nsisLicense.js","sourceRoot":"","sources":["../../../src/targets/nsis/nsisLicense.ts"],"names":[],"mappings":";;;AAAA,4CAAuC;AACvC,gDAAgF;AAChF,6BAA4B;AAI5B,yCAA6C;AAEtC,KAAK,UAAU,kBAAkB,CAAC,QAAqB,EAAE,OAAoB,EAAE,eAAoC,EAAE,SAAwB;IAClJ,MAAM,OAAO,GAAG,MAAM,IAAA,oCAA0B,EAAC,OAAO,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAA;IAC3E,IAAI,OAAO,IAAI,IAAI,EAAE,CAAC;QACpB,IAAI,WAA0B,CAAA;QAC9B,IAAI,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;YAC9B,WAAW,GAAG;gBACZ,kDAAkD;gBAClD,sBAAsB;gBACtB,0CAA0C;gBAC1C,2BAA2B;gBAC3B,+DAA+D;gBAC/D,aAAa;gBAEb,kCAAkC,IAAI,CAAC,IAAI,CAAC,2BAAgB,EAAE,mBAAmB,CAAC,GAAG;aACtF,CAAA;QACH,CAAC;aAAM,CAAC;YACN,WAAW,GAAG,CAAC,kCAAkC,OAAO,GAAG,CAAC,CAAA;QAC9D,CAAC;QAED,eAAe,CAAC,KAAK,CAAC,aAAa,EAAE,WAAW,CAAC,CAAA;QACjD,IAAI,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;YAC9B,eAAe,CAAC,KAAK,CAAC,iBAAiB,EAAE,CAAC,0CAA0C,OAAO,GAAG,CAAC,CAAC,CAAA;QAClG,CAAC;QACD,OAAM;IACR,CAAC;IAED,MAAM,YAAY,GAAG,MAAM,IAAA,yBAAe,EAAC,QAAQ,CAAC,CAAA;IACpD,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9B,OAAM;IACR,CAAC;IAED,MAAM,WAAW,GAAkB,EAAE,CAAA;IACrC,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,CAAA;IAE3C,IAAI,WAAW,GAAkB,IAAI,CAAA;IACrC,KAAK,MAAM,IAAI,IAAI,YAAY,EAAE,CAAC;QAChC,gBAAgB,CAAC,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,CAAA;QAC5C,IAAI,WAAW,IAAI,IAAI,EAAE,CAAC;YACxB,WAAW,GAAG,IAAI,CAAC,IAAI,CAAA;QACzB,CAAC;QACD,WAAW,CAAC,IAAI,CAAC,gCAAgC,YAAI,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,GAAG,CAAC,CAAA;IAC3G,CAAC;IAED,KAAK,MAAM,CAAC,IAAI,gBAAgB,EAAE,CAAC;QACjC,WAAW,CAAC,IAAI,CAAC,gCAAgC,YAAI,CAAC,CAAC,CAAC,KAAK,WAAW,GAAG,CAAC,CAAA;IAC9E,CAAC;IAED,WAAW,CAAC,IAAI,CAAC,+CAA+C,CAAC,CAAA;IACjE,eAAe,CAAC,KAAK,CAAC,aAAa,EAAE,WAAW,CAAC,CAAA;AACnD,CAAC;AAjDD,gDAiDC","sourcesContent":["import { lcid } from \"../../util/langs\"\nimport { getLicenseFiles, getNotLocalizedLicenseFile } from \"../../util/license\"\nimport * as path from \"path\"\nimport { WinPackager } from \"../../winPackager\"\nimport { NsisOptions } from \"./nsisOptions\"\nimport { NsisScriptGenerator } from \"./nsisScriptGenerator\"\nimport { nsisTemplatesDir } from \"./nsisUtil\"\n\nexport async function computeLicensePage(packager: WinPackager, options: NsisOptions, scriptGenerator: NsisScriptGenerator, languages: Array<string>): Promise<void> {\n const license = await getNotLocalizedLicenseFile(options.license, packager)\n if (license != null) {\n let licensePage: Array<string>\n if (license.endsWith(\".html\")) {\n licensePage = [\n \"!define MUI_PAGE_CUSTOMFUNCTION_SHOW LicenseShow\",\n \"Function LicenseShow\",\n \" FindWindow $R0 `#32770` `` $HWNDPARENT\",\n \" GetDlgItem $R0 $R0 1000\",\n \"EmbedHTML::Load /replace $R0 file://$PLUGINSDIR\\\\license.html\",\n \"FunctionEnd\",\n\n `!insertmacro MUI_PAGE_LICENSE \"${path.join(nsisTemplatesDir, \"empty-license.txt\")}\"`,\n ]\n } else {\n licensePage = [`!insertmacro MUI_PAGE_LICENSE \"${license}\"`]\n }\n\n scriptGenerator.macro(\"licensePage\", licensePage)\n if (license.endsWith(\".html\")) {\n scriptGenerator.macro(\"addLicenseFiles\", [`File /oname=$PLUGINSDIR\\\\license.html \"${license}\"`])\n }\n return\n }\n\n const licenseFiles = await getLicenseFiles(packager)\n if (licenseFiles.length === 0) {\n return\n }\n\n const licensePage: Array<string> = []\n const unspecifiedLangs = new Set(languages)\n\n let defaultFile: string | null = null\n for (const item of licenseFiles) {\n unspecifiedLangs.delete(item.langWithRegion)\n if (defaultFile == null) {\n defaultFile = item.file\n }\n licensePage.push(`LicenseLangString MUILicense ${lcid[item.langWithRegion] || item.lang} \"${item.file}\"`)\n }\n\n for (const l of unspecifiedLangs) {\n licensePage.push(`LicenseLangString MUILicense ${lcid[l]} \"${defaultFile}\"`)\n }\n\n licensePage.push('!insertmacro MUI_PAGE_LICENSE \"$(MUILicense)\"')\n scriptGenerator.macro(\"licensePage\", licensePage)\n}\n"]}

View File

@ -0,0 +1,221 @@
import { TargetSpecificOptions } from "../../core";
import { CommonWindowsInstallerConfiguration } from "../..";
interface CustomNsisBinary {
/**
* @default https://github.com/electron-userland/electron-builder-binaries/releases/download
*/
readonly url: string | null;
/**
* @default VKMiizYdmNdJOWpRGz4trl4lD++BvYP2irAXpMilheUP0pc93iKlWAoP843Vlraj8YG19CVn0j+dCo/hURz9+Q==
*/
readonly checksum?: string | null;
/**
* @default 3.0.4.1
*/
readonly version?: string | null;
/**
* Whether or not to enable NSIS logging for debugging.
* Note: Requires a debug-enabled NSIS build.
* electron-builder's included `makensis` does not natively support debug-enabled NSIS installers currently, you must supply your own via `customNsisBinary?: CustomNsisBinary`
* In your custom nsis scripts, you can leverage this functionality via `LogSet` and `LogText`
*/
readonly debugLogging?: boolean | null;
}
export interface CommonNsisOptions {
/**
* Whether to create [Unicode installer](http://nsis.sourceforge.net/Docs/Chapter1.html#intro-unicode).
* @default true
*/
readonly unicode?: boolean;
/**
* See [GUID vs Application Name](../configuration/nsis#guid-vs-application-name).
*/
readonly guid?: string | null;
/**
* If `warningsAsErrors` is `true` (default): NSIS will treat warnings as errors. If `warningsAsErrors` is `false`: NSIS will allow warnings.
* @default true
*/
readonly warningsAsErrors?: boolean;
/**
* @private
* @default false
*/
readonly useZip?: boolean;
/**
* Allows you to provide your own `makensis`, such as one with support for debug logging via LogSet and LogText. (Logging also requires option `debugLogging = true`)
*/
readonly customNsisBinary?: CustomNsisBinary | null;
}
export interface NsisOptions extends CommonNsisOptions, CommonWindowsInstallerConfiguration, TargetSpecificOptions {
/**
* Whether to create one-click installer or assisted.
* @default true
*/
readonly oneClick?: boolean;
/**
* Whether to show install mode installer page (choice per-machine or per-user) for assisted installer. Or whether installation always per all users (per-machine).
*
* If `oneClick` is `true` (default): Whether to install per all users (per-machine).
*
* If `oneClick` is `false` and `perMachine` is `true`: no install mode installer page, always install per-machine.
*
* If `oneClick` is `false` and `perMachine` is `false` (default): install mode installer page.
* @default false
*/
readonly perMachine?: boolean;
/**
* Whether to set per-machine or per-user installation as default selection on the install mode installer page.
*
* @default false
*/
readonly selectPerMachineByDefault?: boolean;
/**
* *assisted installer only.* Allow requesting for elevation. If false, user will have to restart installer with elevated permissions.
* @default true
*/
readonly allowElevation?: boolean;
/**
* *assisted installer only.* Whether to allow user to change installation directory.
* @default false
*/
readonly allowToChangeInstallationDirectory?: boolean;
/**
* *assisted installer only.* remove the default uninstall welcome page.
* @default false
*/
readonly removeDefaultUninstallWelcomePage?: boolean;
/**
* The path to installer icon, relative to the [build resources](/configuration/configuration#MetadataDirectories-buildResources) or to the project directory.
* Defaults to `build/installerIcon.ico` or application icon.
*/
readonly installerIcon?: string | null;
/**
* The path to uninstaller icon, relative to the [build resources](/configuration/configuration#MetadataDirectories-buildResources) or to the project directory.
* Defaults to `build/uninstallerIcon.ico` or application icon.
*/
readonly uninstallerIcon?: string | null;
/**
* *assisted installer only.* `MUI_HEADERIMAGE`, relative to the [build resources](/configuration/configuration#MetadataDirectories-buildResources) or to the project directory.
* @default build/installerHeader.bmp
*/
readonly installerHeader?: string | null;
/**
* *one-click installer only.* The path to header icon (above the progress bar), relative to the [build resources](/configuration/configuration#MetadataDirectories-buildResources) or to the project directory.
* Defaults to `build/installerHeaderIcon.ico` or application icon.
*/
readonly installerHeaderIcon?: string | null;
/**
* *assisted installer only.* `MUI_WELCOMEFINISHPAGE_BITMAP`, relative to the [build resources](/configuration/configuration#MetadataDirectories-buildResources) or to the project directory.
* Defaults to `build/installerSidebar.bmp` or `${NSISDIR}\\Contrib\\Graphics\\Wizard\\nsis3-metro.bmp`. Image size 164 × 314 pixels.
*/
readonly installerSidebar?: string | null;
/**
* *assisted installer only.* `MUI_UNWELCOMEFINISHPAGE_BITMAP`, relative to the [build resources](/configuration/configuration#MetadataDirectories-buildResources) or to the project directory.
* Defaults to `installerSidebar` option or `build/uninstallerSidebar.bmp` or `build/installerSidebar.bmp` or `${NSISDIR}\\Contrib\\Graphics\\Wizard\\nsis3-metro.bmp`
*/
readonly uninstallerSidebar?: string | null;
/**
* The uninstaller display name in the control panel.
* @default ${productName} ${version}
*/
readonly uninstallDisplayName?: string;
/**
* The path to NSIS include script to customize installer. Defaults to `build/installer.nsh`. See [Custom NSIS script](#custom-nsis-script).
*/
readonly include?: string | null;
/**
* The path to NSIS script to customize installer. Defaults to `build/installer.nsi`. See [Custom NSIS script](#custom-nsis-script).
*/
readonly script?: string | null;
/**
* The path to EULA license file. Defaults to `license.txt` or `eula.txt` (or uppercase variants). In addition to `txt`, `rtf` and `html` supported (don't forget to use `target="_blank"` for links).
*
* Multiple license files in different languages are supported — use lang postfix (e.g. `_de`, `_ru`). For example, create files `license_de.txt` and `license_en.txt` in the build resources.
* If OS language is german, `license_de.txt` will be displayed. See map of [language code to name](https://github.com/meikidd/iso-639-1/blob/master/src/data.js).
*
* Appropriate license file will be selected by user OS language.
*/
readonly license?: string | null;
/**
* The [artifact file name template](/configuration/configuration#artifact-file-name-template). Defaults to `${productName} Setup ${version}.${ext}`.
*/
readonly artifactName?: string | null;
/**
* *one-click installer only.* Whether to delete app data on uninstall.
* @default false
*/
readonly deleteAppDataOnUninstall?: boolean;
/**
* @private
*/
differentialPackage?: boolean;
/**
* Whether to display a language selection dialog. Not recommended (by default will be detected using OS language).
* @default false
*/
readonly displayLanguageSelector?: boolean;
/**
* The installer languages (e.g. `en_US`, `de_DE`). Change only if you understand what do you do and for what.
*/
readonly installerLanguages?: Array<string> | string | null;
/**
* [LCID Dec](https://msdn.microsoft.com/en-au/goglobal/bb964664.aspx), defaults to `1033`(`English - United States`).
*/
readonly language?: string | null;
/**
* Whether to create multi-language installer. Defaults to `unicode` option value.
*/
readonly multiLanguageInstaller?: boolean;
/**
* Whether to pack the elevate executable (required for electron-updater if per-machine installer used or can be used in the future). Ignored if `perMachine` is set to `true`.
* @default true
*/
readonly packElevateHelper?: boolean;
/**
* The file extension of files that will be not compressed. Applicable only for `extraResources` and `extraFiles` files.
* @default [".avi", ".mov", ".m4v", ".mp4", ".m4p", ".qt", ".mkv", ".webm", ".vmdk"]
*/
readonly preCompressedFileExtensions?: Array<string> | string | null;
}
/**
* Portable options.
*/
export interface PortableOptions extends TargetSpecificOptions, CommonNsisOptions {
/**
* The [requested execution level](http://nsis.sourceforge.net/Reference/RequestExecutionLevel) for Windows.
* @default user
*/
readonly requestExecutionLevel?: "user" | "highest" | "admin";
/**
* The unpack directory for the portable app resources.
*
* If set to a string, it will be the name in [TEMP](https://www.askvg.com/where-does-windows-store-temporary-files-and-how-to-change-temp-folder-location/) directory
* If set explicitly to `false`, it will use the Windows temp directory ($PLUGINSDIR) that is unique to each launch of the portable application.
*
* Defaults to [uuid](https://github.com/segmentio/ksuid) of build (changed on each build of portable executable).
*/
readonly unpackDirName?: string | boolean;
/**
* The image to show while the portable executable is extracting. This image must be a bitmap (`.bmp`) image.
*/
readonly splashImage?: string | null;
}
/**
* Web Installer options.
*/
export interface NsisWebOptions extends NsisOptions {
/**
* The application package download URL. Optional — by default computed using publish configuration.
*
* URL like `https://example.com/download/latest` allows web installer to be version independent (installer will download latest application package).
* Please note — it is [full URL](https://github.com/electron-userland/electron-builder/issues/1810#issuecomment-317650878).
*
* Custom `X-Arch` http header is set to `32` or `64`.
*/
readonly appPackageUrl?: string | null;
/**
* The [artifact file name template](/configuration/configuration#artifact-file-name-template). Defaults to `${productName} Web Setup ${version}.${ext}`.
*/
readonly artifactName?: string | null;
}
export {};

View File

@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=nsisOptions.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,11 @@
export declare class NsisScriptGenerator {
private readonly lines;
addIncludeDir(file: string): void;
addPluginDir(pluginArch: string, dir: string): void;
include(file: string): void;
macro(name: string, lines: Array<string> | NsisScriptGenerator): void;
file(outputName: string | null, file: string): void;
insertMacro(name: string, parameters: string): void;
flags(flags: Array<string>): void;
build(): string;
}

View File

@ -0,0 +1,52 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.NsisScriptGenerator = void 0;
class NsisScriptGenerator {
constructor() {
this.lines = [];
}
addIncludeDir(file) {
this.lines.push(`!addincludedir "${file}"`);
}
addPluginDir(pluginArch, dir) {
this.lines.push(`!addplugindir /${pluginArch} "${dir}"`);
}
include(file) {
this.lines.push(`!include "${file}"`);
}
macro(name, lines) {
this.lines.push(`!macro ${name}`, ` ${(Array.isArray(lines) ? lines : lines.lines).join("\n ")}`, `!macroend\n`);
}
file(outputName, file) {
this.lines.push(`File${outputName == null ? "" : ` "/oname=${outputName}"`} "${file}"`);
}
insertMacro(name, parameters) {
this.lines.push(`!insertmacro ${name} ${parameters}`);
}
// without -- !!!
flags(flags) {
for (const flagName of flags) {
const variableName = getVarNameForFlag(flagName).replace(/[-]+(\w|$)/g, (m, p1) => p1.toUpperCase());
this.lines.push(`!macro _${variableName} _a _b _t _f
$\{StdUtils.TestParameter} $R9 "${flagName}"
StrCmp "$R9" "true" \`$\{_t}\` \`$\{_f}\`
!macroend
!define ${variableName} \`"" ${variableName} ""\`
`);
}
}
build() {
return this.lines.join("\n") + "\n";
}
}
exports.NsisScriptGenerator = NsisScriptGenerator;
function getVarNameForFlag(flagName) {
if (flagName === "allusers") {
return "isForAllUsers";
}
if (flagName === "currentuser") {
return "isForCurrentUser";
}
return "is" + flagName[0].toUpperCase() + flagName.substring(1);
}
//# sourceMappingURL=nsisScriptGenerator.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"nsisScriptGenerator.js","sourceRoot":"","sources":["../../../src/targets/nsis/nsisScriptGenerator.ts"],"names":[],"mappings":";;;AAAA,MAAa,mBAAmB;IAAhC;QACmB,UAAK,GAAkB,EAAE,CAAA;IA0C5C,CAAC;IAxCC,aAAa,CAAC,IAAY;QACxB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,mBAAmB,IAAI,GAAG,CAAC,CAAA;IAC7C,CAAC;IAED,YAAY,CAAC,UAAkB,EAAE,GAAW;QAC1C,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,kBAAkB,UAAU,KAAK,GAAG,GAAG,CAAC,CAAA;IAC1D,CAAC;IAED,OAAO,CAAC,IAAY;QAClB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,aAAa,IAAI,GAAG,CAAC,CAAA;IACvC,CAAC;IAED,KAAK,CAAC,IAAY,EAAE,KAA0C;QAC5D,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,IAAI,EAAE,EAAE,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,aAAa,CAAC,CAAA;IACpH,CAAC;IAED,IAAI,CAAC,UAAyB,EAAE,IAAY;QAC1C,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,UAAU,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,YAAY,UAAU,GAAG,KAAK,IAAI,GAAG,CAAC,CAAA;IACzF,CAAC;IAED,WAAW,CAAC,IAAY,EAAE,UAAkB;QAC1C,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,gBAAgB,IAAI,IAAI,UAAU,EAAE,CAAC,CAAA;IACvD,CAAC;IAED,iBAAiB;IACjB,KAAK,CAAC,KAAoB;QACxB,KAAK,MAAM,QAAQ,IAAI,KAAK,EAAE,CAAC;YAC7B,MAAM,YAAY,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC,CAAA;YACpG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,YAAY;oCACT,QAAQ;;;UAGlC,YAAY,SAAS,YAAY;CAC1C,CAAC,CAAA;QACE,CAAC;IACH,CAAC;IAED,KAAK;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;IACrC,CAAC;CACF;AA3CD,kDA2CC;AAED,SAAS,iBAAiB,CAAC,QAAgB;IACzC,IAAI,QAAQ,KAAK,UAAU,EAAE,CAAC;QAC5B,OAAO,eAAe,CAAA;IACxB,CAAC;IACD,IAAI,QAAQ,KAAK,aAAa,EAAE,CAAC;QAC/B,OAAO,kBAAkB,CAAA;IAC3B,CAAC;IACD,OAAO,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAA;AACjE,CAAC","sourcesContent":["export class NsisScriptGenerator {\n private readonly lines: Array<string> = []\n\n addIncludeDir(file: string) {\n this.lines.push(`!addincludedir \"${file}\"`)\n }\n\n addPluginDir(pluginArch: string, dir: string) {\n this.lines.push(`!addplugindir /${pluginArch} \"${dir}\"`)\n }\n\n include(file: string) {\n this.lines.push(`!include \"${file}\"`)\n }\n\n macro(name: string, lines: Array<string> | NsisScriptGenerator) {\n this.lines.push(`!macro ${name}`, ` ${(Array.isArray(lines) ? lines : lines.lines).join(\"\\n \")}`, `!macroend\\n`)\n }\n\n file(outputName: string | null, file: string) {\n this.lines.push(`File${outputName == null ? \"\" : ` \"/oname=${outputName}\"`} \"${file}\"`)\n }\n\n insertMacro(name: string, parameters: string) {\n this.lines.push(`!insertmacro ${name} ${parameters}`)\n }\n\n // without -- !!!\n flags(flags: Array<string>) {\n for (const flagName of flags) {\n const variableName = getVarNameForFlag(flagName).replace(/[-]+(\\w|$)/g, (m, p1) => p1.toUpperCase())\n this.lines.push(`!macro _${variableName} _a _b _t _f\n $\\{StdUtils.TestParameter} $R9 \"${flagName}\"\n StrCmp \"$R9\" \"true\" \\`$\\{_t}\\` \\`$\\{_f}\\`\n!macroend\n!define ${variableName} \\`\"\" ${variableName} \"\"\\`\n`)\n }\n }\n\n build() {\n return this.lines.join(\"\\n\") + \"\\n\"\n }\n}\n\nfunction getVarNameForFlag(flagName: string): string {\n if (flagName === \"allusers\") {\n return \"isForAllUsers\"\n }\n if (flagName === \"currentuser\") {\n return \"isForCurrentUser\"\n }\n return \"is\" + flagName[0].toUpperCase() + flagName.substring(1)\n}\n"]}

View File

@ -0,0 +1,31 @@
import { Arch } from "builder-util";
import { PackageFileInfo } from "builder-util-runtime";
import { NsisTarget } from "./NsisTarget";
import { NsisOptions } from "./nsisOptions";
export declare const nsisTemplatesDir: string;
export declare const NsisTargetOptions: {
then: (callback: (options: NsisOptions) => any) => Promise<string>;
resolve: (options: NsisOptions) => any;
};
export declare const NSIS_PATH: () => Promise<string>;
export interface PackArchResult {
fileInfo: PackageFileInfo;
unpackedSize: number;
}
export declare class AppPackageHelper {
private readonly elevateHelper;
private readonly archToResult;
private readonly infoToIsDelete;
/** @private */
refCount: number;
constructor(elevateHelper: CopyElevateHelper);
packArch(arch: Arch, target: NsisTarget): Promise<PackArchResult>;
finishBuild(): Promise<any>;
}
export declare class CopyElevateHelper {
private readonly copied;
copy(appOutDir: string, target: NsisTarget): Promise<any>;
}
export declare class UninstallerReader {
static exec(installerPath: string, uninstallerPath: string): Promise<void>;
}

View File

@ -0,0 +1,251 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.UninstallerReader = exports.CopyElevateHelper = exports.AppPackageHelper = exports.NSIS_PATH = exports.NsisTargetOptions = exports.nsisTemplatesDir = void 0;
const builder_util_1 = require("builder-util");
const binDownload_1 = require("../../binDownload");
const fs_1 = require("builder-util/out/fs");
const path = require("path");
const pathManager_1 = require("../../util/pathManager");
const fs = require("fs/promises");
const zlib = require("zlib");
exports.nsisTemplatesDir = (0, pathManager_1.getTemplatePath)("nsis");
exports.NsisTargetOptions = (() => {
let _resolve;
const promise = new Promise(resolve => (_resolve = resolve));
return {
then: (callback) => promise.then(callback),
resolve: (options) => _resolve(options),
};
})();
const NSIS_PATH = () => {
const custom = process.env.ELECTRON_BUILDER_NSIS_DIR;
if (custom != null && custom.length > 0) {
return Promise.resolve(custom.trim());
}
return exports.NsisTargetOptions.then((options) => {
if (options.customNsisBinary) {
const { checksum, url, version } = options.customNsisBinary;
if (checksum && url) {
const binaryVersion = version || checksum.substr(0, 8);
return (0, binDownload_1.getBinFromCustomLoc)("nsis", binaryVersion, url, checksum);
}
}
// Warning: Don't use v3.0.4.2 - https://github.com/electron-userland/electron-builder/issues/6334
// noinspection SpellCheckingInspection
return (0, binDownload_1.getBinFromUrl)("nsis", "3.0.4.1", "VKMiizYdmNdJOWpRGz4trl4lD++BvYP2irAXpMilheUP0pc93iKlWAoP843Vlraj8YG19CVn0j+dCo/hURz9+Q==");
});
};
exports.NSIS_PATH = NSIS_PATH;
class AppPackageHelper {
constructor(elevateHelper) {
this.elevateHelper = elevateHelper;
this.archToResult = new Map();
this.infoToIsDelete = new Map();
/** @private */
this.refCount = 0;
}
async packArch(arch, target) {
let resultPromise = this.archToResult.get(arch);
if (resultPromise == null) {
const appOutDir = target.archs.get(arch);
resultPromise = this.elevateHelper
.copy(appOutDir, target)
.then(() => target.buildAppPackage(appOutDir, arch))
.then(async (fileInfo) => ({
fileInfo,
unpackedSize: await (0, fs_1.dirSize)(appOutDir),
}));
this.archToResult.set(arch, resultPromise);
}
const result = await resultPromise;
const { fileInfo: info } = result;
if (target.isWebInstaller) {
this.infoToIsDelete.set(info, false);
}
else if (!this.infoToIsDelete.has(info)) {
this.infoToIsDelete.set(info, true);
}
return result;
}
async finishBuild() {
if (--this.refCount > 0) {
return;
}
const filesToDelete = [];
for (const [info, isDelete] of this.infoToIsDelete.entries()) {
if (isDelete) {
filesToDelete.push(info.path);
}
}
await Promise.all(filesToDelete.map(it => fs.unlink(it)));
}
}
exports.AppPackageHelper = AppPackageHelper;
class CopyElevateHelper {
constructor() {
this.copied = new Map();
}
copy(appOutDir, target) {
if (!target.packager.info.framework.isCopyElevateHelper) {
return Promise.resolve();
}
let isPackElevateHelper = target.options.packElevateHelper;
if (isPackElevateHelper === false && target.options.perMachine === true) {
isPackElevateHelper = true;
builder_util_1.log.warn("`packElevateHelper = false` is ignored, because `perMachine` is set to `true`");
}
if (isPackElevateHelper === false) {
return Promise.resolve();
}
let promise = this.copied.get(appOutDir);
if (promise != null) {
return promise;
}
promise = (0, exports.NSIS_PATH)().then(it => {
const outFile = path.join(appOutDir, "resources", "elevate.exe");
const promise = (0, fs_1.copyFile)(path.join(it, "elevate.exe"), outFile, false);
if (target.packager.platformSpecificBuildOptions.signAndEditExecutable !== false) {
return promise.then(() => target.packager.sign(outFile));
}
return promise;
});
this.copied.set(appOutDir, promise);
return promise;
}
}
exports.CopyElevateHelper = CopyElevateHelper;
class BinaryReader {
constructor(buffer) {
this._buffer = buffer;
this._position = 0;
}
get length() {
return this._buffer.length;
}
get position() {
return this._position;
}
match(signature) {
if (signature.every((v, i) => this._buffer[this._position + i] === v)) {
this._position += signature.length;
return true;
}
return false;
}
skip(offset) {
this._position += offset;
}
bytes(size) {
const value = this._buffer.subarray(this._position, this._position + size);
this._position += size;
return value;
}
uint16() {
const value = this._buffer[this._position] | (this._buffer[this._position + 1] << 8);
this._position += 2;
return value;
}
uint32() {
return this.uint16() | (this.uint16() << 16);
}
string(length) {
let value = "";
for (let i = 0; i < length; i++) {
const c = this._buffer[this._position + i];
if (c === 0x00) {
break;
}
value += String.fromCharCode(c);
}
this._position += length;
return value;
}
}
class UninstallerReader {
// noinspection SpellCheckingInspection
static async exec(installerPath, uninstallerPath) {
const buffer = await fs.readFile(installerPath);
const reader = new BinaryReader(buffer);
// IMAGE_DOS_HEADER
if (!reader.match([0x4d, 0x5a])) {
throw new Error("Invalid 'MZ' signature.");
}
reader.skip(58);
// e_lfanew
reader.skip(reader.uint32() - reader.position);
// IMAGE_FILE_HEADER
if (!reader.match([0x50, 0x45, 0x00, 0x00])) {
throw new Error("Invalid 'PE' signature.");
}
reader.skip(2);
const numberOfSections = reader.uint16();
reader.skip(12);
const sizeOfOptionalHeader = reader.uint16();
reader.skip(2);
reader.skip(sizeOfOptionalHeader);
// IMAGE_SECTION_HEADER
let nsisOffset = 0;
for (let i = 0; i < numberOfSections; i++) {
const name = reader.string(8);
reader.skip(8);
const rawSize = reader.uint32();
const rawPointer = reader.uint32();
reader.skip(16);
switch (name) {
case ".text":
case ".rdata":
case ".data":
case ".rsrc": {
nsisOffset = Math.max(rawPointer + rawSize, nsisOffset);
break;
}
default: {
if (rawPointer !== 0 && rawSize !== 0) {
throw new Error("Unsupported section '" + name + "'.");
}
break;
}
}
}
const executable = buffer.subarray(0, nsisOffset);
const nsisSize = buffer.length - nsisOffset;
const nsisReader = new BinaryReader(buffer.subarray(nsisOffset, nsisOffset + nsisSize));
const nsisSignature = [0xef, 0xbe, 0xad, 0xde, 0x4e, 0x75, 0x6c, 0x6c, 0x73, 0x6f, 0x66, 0x74, 0x49, 0x6e, 0x73, 0x74];
nsisReader.uint32(); // ?
if (!nsisReader.match(nsisSignature)) {
throw new Error("Invalid signature.");
}
nsisReader.uint32(); // ?
if (nsisSize !== nsisReader.uint32()) {
throw new Error("Size mismatch.");
}
let innerBuffer = null;
while (true) {
let size = nsisReader.uint32();
const compressed = (size & 0x80000000) !== 0;
size = size & 0x7fffffff;
if (size === 0 || nsisReader.position + size > nsisReader.length || nsisReader.position >= nsisReader.length) {
break;
}
let buffer = nsisReader.bytes(size);
if (compressed) {
buffer = zlib.inflateRawSync(buffer);
}
const innerReader = new BinaryReader(buffer);
innerReader.uint32(); // ?
if (innerReader.match(nsisSignature)) {
if (innerBuffer) {
throw new Error("Multiple inner blocks.");
}
innerBuffer = buffer;
}
}
if (!innerBuffer) {
throw new Error("Inner block not found.");
}
await fs.writeFile(uninstallerPath, executable);
await fs.appendFile(uninstallerPath, innerBuffer);
}
}
exports.UninstallerReader = UninstallerReader;
//# sourceMappingURL=nsisUtil.js.map

File diff suppressed because one or more lines are too long

15
mc_test/node_modules/app-builder-lib/out/targets/pkg.d.ts generated vendored Executable file
View File

@ -0,0 +1,15 @@
import { Arch } from "builder-util";
import { PkgOptions } from "../options/pkgOptions";
import { Identity } from "../codeSign/macCodeSign";
import { Target } from "../core";
import MacPackager from "../macPackager";
export declare class PkgTarget extends Target {
private readonly packager;
readonly outDir: string;
readonly options: PkgOptions;
constructor(packager: MacPackager, outDir: string);
build(appPath: string, arch: Arch): Promise<any>;
private customizeDistributionConfiguration;
private buildComponentPackage;
}
export declare function prepareProductBuildArgs(identity: Identity | null, keychain: string | null | undefined): Array<string>;

184
mc_test/node_modules/app-builder-lib/out/targets/pkg.js generated vendored Executable file
View File

@ -0,0 +1,184 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.prepareProductBuildArgs = exports.PkgTarget = void 0;
const builder_util_1 = require("builder-util");
const fs_1 = require("builder-util/out/fs");
const appBuilder_1 = require("../util/appBuilder");
const license_1 = require("../util/license");
const promises_1 = require("fs/promises");
const path = require("path");
const appInfo_1 = require("../appInfo");
const macCodeSign_1 = require("../codeSign/macCodeSign");
const core_1 = require("../core");
const fs_2 = require("fs");
const certType = "Developer ID Installer";
// http://www.shanekirk.com/2013/10/creating-flat-packages-in-osx/
// to use --scripts, we must build .app bundle separately using pkgbuild
// productbuild --scripts doesn't work (because scripts in this case not added to our package)
// https://github.com/electron-userland/@electron/osx-sign/issues/96#issuecomment-274986942
class PkgTarget extends core_1.Target {
constructor(packager, outDir) {
super("pkg");
this.packager = packager;
this.outDir = outDir;
this.options = {
allowAnywhere: true,
allowCurrentUserHome: true,
allowRootDirectory: true,
...this.packager.config.pkg,
};
}
async build(appPath, arch) {
const packager = this.packager;
const options = this.options;
const appInfo = packager.appInfo;
// pkg doesn't like not ASCII symbols (Could not open package to list files: /Volumes/test/t-gIjdGK/test-project-0/dist/Test App ßW-1.1.0.pkg)
const artifactName = packager.expandArtifactNamePattern(options, "pkg", arch);
const artifactPath = path.join(this.outDir, artifactName);
await packager.info.callArtifactBuildStarted({
targetPresentableName: "pkg",
file: artifactPath,
arch,
});
const keychainFile = (await packager.codeSigningInfo.value).keychainFile;
const appOutDir = this.outDir;
// https://developer.apple.com/library/content/documentation/DeveloperTools/Reference/DistributionDefinitionRef/Chapters/Distribution_XML_Ref.html
const distInfoFile = path.join(appOutDir, "distribution.xml");
const innerPackageFile = path.join(appOutDir, `${(0, appInfo_1.filterCFBundleIdentifier)(appInfo.id)}.pkg`);
const componentPropertyListFile = path.join(appOutDir, `${(0, appInfo_1.filterCFBundleIdentifier)(appInfo.id)}.plist`);
const identity = (await Promise.all([
(0, macCodeSign_1.findIdentity)(certType, options.identity || packager.platformSpecificBuildOptions.identity, keychainFile),
this.customizeDistributionConfiguration(distInfoFile, appPath),
this.buildComponentPackage(appPath, componentPropertyListFile, innerPackageFile),
]))[0];
if (identity == null && packager.forceCodeSigning) {
throw new Error(`Cannot find valid "${certType}" to sign standalone installer, please see https://electron.build/code-signing`);
}
const args = prepareProductBuildArgs(identity, keychainFile);
args.push("--distribution", distInfoFile);
args.push(artifactPath);
(0, builder_util_1.use)(options.productbuild, it => args.push(...it));
await (0, builder_util_1.exec)("productbuild", args, {
cwd: appOutDir,
});
await Promise.all([(0, promises_1.unlink)(innerPackageFile), (0, promises_1.unlink)(distInfoFile)]);
await packager.dispatchArtifactCreated(artifactPath, this, arch, packager.computeSafeArtifactName(artifactName, "pkg", arch));
}
async customizeDistributionConfiguration(distInfoFile, appPath) {
await (0, builder_util_1.exec)("productbuild", ["--synthesize", "--component", appPath, distInfoFile], {
cwd: this.outDir,
});
const options = this.options;
let distInfo = await (0, promises_1.readFile)(distInfoFile, "utf-8");
if (options.mustClose != null && options.mustClose.length !== 0) {
const startContent = ` <pkg-ref id="${this.packager.appInfo.id}">\n <must-close>\n`;
const endContent = " </must-close>\n </pkg-ref>\n</installer-gui-script>";
let mustCloseContent = "";
options.mustClose.forEach(appId => {
mustCloseContent += ` <app id="${appId}"/>\n`;
});
distInfo = distInfo.replace("</installer-gui-script>", `${startContent}${mustCloseContent}${endContent}`);
}
const insertIndex = distInfo.lastIndexOf("</installer-gui-script>");
distInfo =
distInfo.substring(0, insertIndex) +
` <domains enable_anywhere="${options.allowAnywhere}" enable_currentUserHome="${options.allowCurrentUserHome}" enable_localSystem="${options.allowRootDirectory}" />\n` +
distInfo.substring(insertIndex);
if (options.background != null) {
const background = await this.packager.getResource(options.background.file);
if (background != null) {
const alignment = options.background.alignment || "center";
// noinspection SpellCheckingInspection
const scaling = options.background.scaling || "tofit";
distInfo = distInfo.substring(0, insertIndex) + ` <background file="${background}" alignment="${alignment}" scaling="${scaling}"/>\n` + distInfo.substring(insertIndex);
distInfo =
distInfo.substring(0, insertIndex) + ` <background-darkAqua file="${background}" alignment="${alignment}" scaling="${scaling}"/>\n` + distInfo.substring(insertIndex);
}
}
const welcome = await this.packager.getResource(options.welcome);
if (welcome != null) {
distInfo = distInfo.substring(0, insertIndex) + ` <welcome file="${welcome}"/>\n` + distInfo.substring(insertIndex);
}
const license = await (0, license_1.getNotLocalizedLicenseFile)(options.license, this.packager);
if (license != null) {
distInfo = distInfo.substring(0, insertIndex) + ` <license file="${license}"/>\n` + distInfo.substring(insertIndex);
}
const conclusion = await this.packager.getResource(options.conclusion);
if (conclusion != null) {
distInfo = distInfo.substring(0, insertIndex) + ` <conclusion file="${conclusion}"/>\n` + distInfo.substring(insertIndex);
}
(0, builder_util_1.debug)(distInfo);
await (0, promises_1.writeFile)(distInfoFile, distInfo);
}
async buildComponentPackage(appPath, propertyListOutputFile, packageOutputFile) {
var _a;
const options = this.options;
const rootPath = path.dirname(appPath);
// first produce a component plist template
await (0, builder_util_1.exec)("pkgbuild", ["--analyze", "--root", rootPath, propertyListOutputFile]);
// process the template plist
const plistInfo = (await (0, appBuilder_1.executeAppBuilderAsJson)(["decode-plist", "-f", propertyListOutputFile]))[0].filter((it) => it.RootRelativeBundlePath !== "Electron.dSYM");
let packageInfo = {};
if (plistInfo.length > 0) {
packageInfo = plistInfo[0];
// ChildBundles lists all of electron binaries within the .app.
// There is no particular reason for removing that key, except to be as close as possible to
// the PackageInfo generated by previous versions of electron-builder.
delete packageInfo.ChildBundles;
if (options.isRelocatable != null) {
packageInfo.BundleIsRelocatable = options.isRelocatable;
}
if (options.isVersionChecked != null) {
packageInfo.BundleIsVersionChecked = options.isVersionChecked;
}
if (options.hasStrictIdentifier != null) {
packageInfo.BundleHasStrictIdentifier = options.hasStrictIdentifier;
}
if (options.overwriteAction != null) {
packageInfo.BundleOverwriteAction = options.overwriteAction;
}
}
// now build the package
const args = ["--root", rootPath, "--identifier", this.packager.appInfo.id, "--component-plist", propertyListOutputFile];
(0, builder_util_1.use)(this.options.installLocation || "/Applications", it => args.push("--install-location", it));
// nasty nested ternary-statement, probably should optimize
const scriptsDir =
// user-provided scripts dir
options.scripts != null
? path.resolve(this.packager.info.buildResourcesDir, options.scripts)
: // fallback to default unless user explicitly sets null
options.scripts !== null
? path.join(this.packager.info.buildResourcesDir, "pkg-scripts")
: null;
if (scriptsDir && ((_a = (await (0, fs_1.statOrNull)(scriptsDir))) === null || _a === void 0 ? void 0 : _a.isDirectory())) {
const dirContents = (0, fs_2.readdirSync)(scriptsDir);
dirContents.forEach(name => {
if (name.includes("preinstall")) {
packageInfo.BundlePreInstallScriptPath = name;
}
else if (name.includes("postinstall")) {
packageInfo.BundlePostInstallScriptPath = name;
}
});
args.push("--scripts", scriptsDir);
}
if (plistInfo.length > 0) {
await (0, appBuilder_1.executeAppBuilderAndWriteJson)(["encode-plist"], { [propertyListOutputFile]: plistInfo });
}
args.push(packageOutputFile);
await (0, builder_util_1.exec)("pkgbuild", args);
}
}
exports.PkgTarget = PkgTarget;
function prepareProductBuildArgs(identity, keychain) {
const args = [];
if (identity != null) {
args.push("--sign", identity.hash);
if (keychain != null) {
args.push("--keychain", keychain);
}
}
return args;
}
exports.prepareProductBuildArgs = prepareProductBuildArgs;
//# sourceMappingURL=pkg.js.map

File diff suppressed because one or more lines are too long

17
mc_test/node_modules/app-builder-lib/out/targets/snap.d.ts generated vendored Executable file
View File

@ -0,0 +1,17 @@
import { Arch } from "builder-util";
import { Target } from "../core";
import { LinuxPackager } from "../linuxPackager";
import { SnapOptions } from "../options/SnapOptions";
import { LinuxTargetHelper } from "./LinuxTargetHelper";
export default class SnapTarget extends Target {
private readonly packager;
private readonly helper;
readonly outDir: string;
readonly options: SnapOptions;
isUseTemplateApp: boolean;
constructor(name: string, packager: LinuxPackager, helper: LinuxTargetHelper, outDir: string);
private replaceDefault;
private createDescriptor;
build(appOutDir: string, arch: Arch): Promise<any>;
private isElectronVersionGreaterOrEqualThan;
}

322
mc_test/node_modules/app-builder-lib/out/targets/snap.js generated vendored Executable file
View File

@ -0,0 +1,322 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const builder_util_1 = require("builder-util");
const builder_util_runtime_1 = require("builder-util-runtime");
const fs_extra_1 = require("fs-extra");
const js_yaml_1 = require("js-yaml");
const path = require("path");
const semver = require("semver");
const core_1 = require("../core");
const pathManager_1 = require("../util/pathManager");
const targetUtil_1 = require("./targetUtil");
const defaultPlugs = ["desktop", "desktop-legacy", "home", "x11", "wayland", "unity7", "browser-support", "network", "gsettings", "audio-playback", "pulseaudio", "opengl"];
class SnapTarget extends core_1.Target {
constructor(name, packager, helper, outDir) {
super(name);
this.packager = packager;
this.helper = helper;
this.outDir = outDir;
this.options = { ...this.packager.platformSpecificBuildOptions, ...this.packager.config[this.name] };
this.isUseTemplateApp = false;
}
replaceDefault(inList, defaultList) {
const result = (0, builder_util_1.replaceDefault)(inList, defaultList);
if (result !== defaultList) {
this.isUseTemplateApp = false;
}
return result;
}
async createDescriptor(arch) {
if (!this.isElectronVersionGreaterOrEqualThan("4.0.0")) {
if (!this.isElectronVersionGreaterOrEqualThan("2.0.0-beta.1")) {
throw new builder_util_1.InvalidConfigurationError("Electron 2 and higher is required to build Snap");
}
builder_util_1.log.warn("Electron 4 and higher is highly recommended for Snap");
}
const appInfo = this.packager.appInfo;
const snapName = this.packager.executableName.toLowerCase();
const options = this.options;
const plugs = normalizePlugConfiguration(this.options.plugs);
const plugNames = this.replaceDefault(plugs == null ? null : Object.getOwnPropertyNames(plugs), defaultPlugs);
const slots = normalizePlugConfiguration(this.options.slots);
const buildPackages = (0, builder_util_runtime_1.asArray)(options.buildPackages);
const defaultStagePackages = getDefaultStagePackages();
const stagePackages = this.replaceDefault(options.stagePackages, defaultStagePackages);
this.isUseTemplateApp =
this.options.useTemplateApp !== false &&
(arch === builder_util_1.Arch.x64 || arch === builder_util_1.Arch.armv7l) &&
buildPackages.length === 0 &&
isArrayEqualRegardlessOfSort(stagePackages, defaultStagePackages);
const appDescriptor = {
command: "command.sh",
plugs: plugNames,
adapter: "none",
};
const snap = (0, js_yaml_1.load)(await (0, fs_extra_1.readFile)(path.join((0, pathManager_1.getTemplatePath)("snap"), "snapcraft.yaml"), "utf-8"));
if (this.isUseTemplateApp) {
delete appDescriptor.adapter;
}
if (options.base != null) {
snap.base = options.base;
// from core22 onwards adapter is legacy
if (Number(snap.base.split("core")[1]) >= 22) {
delete appDescriptor.adapter;
}
}
if (options.grade != null) {
snap.grade = options.grade;
}
if (options.confinement != null) {
snap.confinement = options.confinement;
}
if (options.appPartStage != null) {
snap.parts.app.stage = options.appPartStage;
}
if (options.layout != null) {
snap.layout = options.layout;
}
if (slots != null) {
appDescriptor.slots = Object.getOwnPropertyNames(slots);
for (const slotName of appDescriptor.slots) {
const slotOptions = slots[slotName];
if (slotOptions == null) {
continue;
}
if (!snap.slots) {
snap.slots = {};
}
snap.slots[slotName] = slotOptions;
}
}
(0, builder_util_1.deepAssign)(snap, {
name: snapName,
version: appInfo.version,
title: options.title || appInfo.productName,
summary: options.summary || appInfo.productName,
compression: options.compression,
description: this.helper.getDescription(options),
architectures: [(0, builder_util_1.toLinuxArchString)(arch, "snap")],
apps: {
[snapName]: appDescriptor,
},
parts: {
app: {
"stage-packages": stagePackages,
},
},
});
if (options.autoStart) {
appDescriptor.autostart = `${snap.name}.desktop`;
}
if (options.confinement === "classic") {
delete appDescriptor.plugs;
delete snap.plugs;
}
else {
const archTriplet = archNameToTriplet(arch);
appDescriptor.environment = {
DISABLE_WAYLAND: options.allowNativeWayland ? "" : "1",
PATH: "$SNAP/usr/sbin:$SNAP/usr/bin:$SNAP/sbin:$SNAP/bin:$PATH",
SNAP_DESKTOP_RUNTIME: "$SNAP/gnome-platform",
LD_LIBRARY_PATH: [
"$SNAP_LIBRARY_PATH",
"$SNAP/lib:$SNAP/usr/lib:$SNAP/lib/" + archTriplet + ":$SNAP/usr/lib/" + archTriplet,
"$LD_LIBRARY_PATH:$SNAP/lib:$SNAP/usr/lib",
"$SNAP/lib/" + archTriplet + ":$SNAP/usr/lib/" + archTriplet,
].join(":"),
...options.environment,
};
if (plugs != null) {
for (const plugName of plugNames) {
const plugOptions = plugs[plugName];
if (plugOptions == null) {
continue;
}
snap.plugs[plugName] = plugOptions;
}
}
}
if (buildPackages.length > 0) {
snap.parts.app["build-packages"] = buildPackages;
}
if (options.after != null) {
snap.parts.app.after = options.after;
}
if (options.assumes != null) {
snap.assumes = (0, builder_util_runtime_1.asArray)(options.assumes);
}
return snap;
}
async build(appOutDir, arch) {
var _a;
const packager = this.packager;
const options = this.options;
// tslint:disable-next-line:no-invalid-template-strings
const artifactName = packager.expandArtifactNamePattern(this.options, "snap", arch, "${name}_${version}_${arch}.${ext}", false);
const artifactPath = path.join(this.outDir, artifactName);
await packager.info.callArtifactBuildStarted({
targetPresentableName: "snap",
file: artifactPath,
arch,
});
const snap = await this.createDescriptor(arch);
const stageDir = await (0, targetUtil_1.createStageDirPath)(this, packager, arch);
const snapArch = (0, builder_util_1.toLinuxArchString)(arch, "snap");
const args = ["snap", "--app", appOutDir, "--stage", stageDir, "--arch", snapArch, "--output", artifactPath, "--executable", this.packager.executableName];
await this.helper.icons;
if (this.helper.maxIconPath != null) {
if (!this.isUseTemplateApp) {
snap.icon = "snap/gui/icon.png";
}
args.push("--icon", this.helper.maxIconPath);
}
// snapcraft.yaml inside a snap directory
const snapMetaDir = path.join(stageDir, this.isUseTemplateApp ? "meta" : "snap");
const desktopFile = path.join(snapMetaDir, "gui", `${snap.name}.desktop`);
await this.helper.writeDesktopEntry(this.options, packager.executableName + " %U", desktopFile, {
// tslint:disable:no-invalid-template-strings
Icon: "${SNAP}/meta/gui/icon.png",
});
const extraAppArgs = (_a = options.executableArgs) !== null && _a !== void 0 ? _a : [];
if (this.isElectronVersionGreaterOrEqualThan("5.0.0") && !isBrowserSandboxAllowed(snap)) {
const noSandboxArg = "--no-sandbox";
if (!extraAppArgs.includes(noSandboxArg)) {
extraAppArgs.push(noSandboxArg);
}
if (this.isUseTemplateApp) {
args.push("--exclude", "chrome-sandbox");
}
}
if (extraAppArgs.length > 0) {
args.push("--extraAppArgs=" + extraAppArgs.join(" "));
}
if (snap.compression != null) {
args.push("--compression", snap.compression);
}
if (this.isUseTemplateApp) {
// remove fields that are valid in snapcraft.yaml, but not snap.yaml
const fieldsToStrip = ["compression", "contact", "donation", "issues", "parts", "source-code", "website"];
for (const field of fieldsToStrip) {
delete snap[field];
}
}
if (packager.packagerOptions.effectiveOptionComputed != null && (await packager.packagerOptions.effectiveOptionComputed({ snap, desktopFile, args }))) {
return;
}
await (0, fs_extra_1.outputFile)(path.join(snapMetaDir, this.isUseTemplateApp ? "snap.yaml" : "snapcraft.yaml"), (0, builder_util_1.serializeToYaml)(snap));
const hooksDir = await packager.getResource(options.hooks, "snap-hooks");
if (hooksDir != null) {
args.push("--hooks", hooksDir);
}
if (this.isUseTemplateApp) {
args.push("--template-url", `electron4:${snapArch}`);
}
await (0, builder_util_1.executeAppBuilder)(args);
const publishConfig = findSnapPublishConfig(this.packager.config);
await packager.info.callArtifactBuildCompleted({
file: artifactPath,
safeArtifactName: packager.computeSafeArtifactName(artifactName, "snap", arch, false),
target: this,
arch,
packager,
publishConfig: publishConfig == null ? { provider: "snapStore" } : publishConfig,
});
}
isElectronVersionGreaterOrEqualThan(version) {
return semver.gte(this.packager.config.electronVersion || "7.0.0", version);
}
}
exports.default = SnapTarget;
function findSnapPublishConfig(config) {
var _a;
if (!config) {
return null;
}
if ((_a = config.linux) === null || _a === void 0 ? void 0 : _a.publish) {
const configCandidate = findSnapPublishConfigInPublishNode(config.linux.publish);
if (configCandidate) {
return configCandidate;
}
}
if (config.publish) {
const configCandidate = findSnapPublishConfigInPublishNode(config.publish);
if (configCandidate) {
return configCandidate;
}
}
return null;
}
function findSnapPublishConfigInPublishNode(configPublishNode) {
if (!configPublishNode) {
return null;
}
if (Array.isArray(configPublishNode)) {
for (const configObj of configPublishNode) {
if (isSnapStoreOptions(configObj)) {
return configObj;
}
}
}
if (typeof configPublishNode === `object` && isSnapStoreOptions(configPublishNode)) {
return configPublishNode;
}
return null;
}
function isSnapStoreOptions(configPublishNode) {
const snapStoreOptionsCandidate = configPublishNode;
return (snapStoreOptionsCandidate === null || snapStoreOptionsCandidate === void 0 ? void 0 : snapStoreOptionsCandidate.provider) === `snapStore`;
}
function archNameToTriplet(arch) {
switch (arch) {
case builder_util_1.Arch.x64:
return "x86_64-linux-gnu";
case builder_util_1.Arch.ia32:
return "i386-linux-gnu";
case builder_util_1.Arch.armv7l:
// noinspection SpellCheckingInspection
return "arm-linux-gnueabihf";
case builder_util_1.Arch.arm64:
return "aarch64-linux-gnu";
default:
throw new Error(`Unsupported arch ${arch}`);
}
}
function isArrayEqualRegardlessOfSort(a, b) {
a = a.slice();
b = b.slice();
a.sort();
b.sort();
return a.length === b.length && a.every((value, index) => value === b[index]);
}
function normalizePlugConfiguration(raw) {
if (raw == null) {
return null;
}
const result = {};
for (const item of Array.isArray(raw) ? raw : [raw]) {
if (typeof item === "string") {
result[item] = null;
}
else {
Object.assign(result, item);
}
}
return result;
}
function isBrowserSandboxAllowed(snap) {
if (snap.plugs != null) {
for (const plugName of Object.keys(snap.plugs)) {
const plug = snap.plugs[plugName];
if (plug.interface === "browser-support" && plug["allow-sandbox"] === true) {
return true;
}
}
}
return false;
}
function getDefaultStagePackages() {
// libxss1 - was "error while loading shared libraries: libXss.so.1" on Xubuntu 16.04
// noinspection SpellCheckingInspection
return ["libnspr4", "libnss3", "libxss1", "libappindicator3-1", "libsecret-1-0"];
}
//# sourceMappingURL=snap.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,12 @@
import { Arch } from "builder-util";
import { Platform, Target } from "../core";
import { PlatformPackager } from "../platformPackager";
export declare function computeArchToTargetNamesMap(raw: Map<Arch, Array<string>>, platformPackager: PlatformPackager<any>, platform: Platform): Map<Arch, Array<string>>;
export declare function createTargets(nameToTarget: Map<string, Target>, rawList: Array<string>, outDir: string, packager: PlatformPackager<any>): Array<Target>;
export declare function createCommonTarget(target: string, outDir: string, packager: PlatformPackager<any>): Target;
export declare class NoOpTarget extends Target {
readonly options: null;
constructor(name: string);
get outDir(): string;
build(appOutDir: string, arch: Arch): Promise<any>;
}

View File

@ -0,0 +1,101 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.NoOpTarget = exports.createCommonTarget = exports.createTargets = exports.computeArchToTargetNamesMap = void 0;
const builder_util_1 = require("builder-util");
const core_1 = require("../core");
const ArchiveTarget_1 = require("./ArchiveTarget");
const archiveTargets = new Set(["zip", "7z", "tar.xz", "tar.lz", "tar.gz", "tar.bz2"]);
function computeArchToTargetNamesMap(raw, platformPackager, platform) {
for (const targetNames of raw.values()) {
if (targetNames.length > 0) {
// https://github.com/electron-userland/electron-builder/issues/1355
return raw;
}
}
const defaultArchs = raw.size === 0 ? [process.arch] : Array.from(raw.keys()).map(it => builder_util_1.Arch[it]);
const result = new Map(raw);
for (const target of (0, builder_util_1.asArray)(platformPackager.platformSpecificBuildOptions.target).map(it => (typeof it === "string" ? { target: it } : it))) {
let name = target.target;
let archs = target.arch;
const suffixPos = name.lastIndexOf(":");
if (suffixPos > 0) {
name = target.target.substring(0, suffixPos);
if (archs == null) {
archs = target.target.substring(suffixPos + 1);
}
}
for (const arch of archs == null ? defaultArchs : (0, builder_util_1.asArray)(archs)) {
(0, builder_util_1.addValue)(result, (0, builder_util_1.archFromString)(arch), name);
}
}
if (result.size === 0) {
const defaultTarget = platformPackager.defaultTarget;
if (raw.size === 0 && platform === core_1.Platform.LINUX && (process.platform === "darwin" || process.platform === "win32")) {
result.set(builder_util_1.Arch.x64, defaultTarget);
// cannot enable arm because of native dependencies - e.g. keytar doesn't provide pre-builds for arm
// result.set(Arch.armv7l, ["snap"])
}
else {
for (const arch of defaultArchs) {
result.set((0, builder_util_1.archFromString)(arch), defaultTarget);
}
}
}
return result;
}
exports.computeArchToTargetNamesMap = computeArchToTargetNamesMap;
function createTargets(nameToTarget, rawList, outDir, packager) {
const result = [];
const mapper = (name, factory) => {
let target = nameToTarget.get(name);
if (target == null) {
target = factory(outDir);
nameToTarget.set(name, target);
}
result.push(target);
};
const targets = normalizeTargets(rawList, packager.defaultTarget);
packager.createTargets(targets, mapper);
return result;
}
exports.createTargets = createTargets;
function normalizeTargets(targets, defaultTarget) {
const list = [];
for (const t of targets) {
const name = t.toLowerCase().trim();
if (name === core_1.DEFAULT_TARGET) {
list.push(...defaultTarget);
}
else {
list.push(name);
}
}
return list;
}
function createCommonTarget(target, outDir, packager) {
if (archiveTargets.has(target)) {
return new ArchiveTarget_1.ArchiveTarget(target, outDir, packager);
}
else if (target === core_1.DIR_TARGET) {
return new NoOpTarget(core_1.DIR_TARGET);
}
else {
throw new Error(`Unknown target: ${target}`);
}
}
exports.createCommonTarget = createCommonTarget;
class NoOpTarget extends core_1.Target {
constructor(name) {
super(name);
this.options = null;
}
get outDir() {
throw new Error("NoOpTarget");
}
// eslint-disable-next-line
async build(appOutDir, arch) {
// no build
}
}
exports.NoOpTarget = NoOpTarget;
//# sourceMappingURL=targetFactory.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,14 @@
import { Target, AppInfo } from "../";
import { Arch } from "builder-util";
import { PlatformPackager } from "../platformPackager";
export declare class StageDir {
readonly dir: string;
constructor(dir: string);
getTempFile(name: string): string;
cleanup(): Promise<void>;
toString(): string;
}
export declare function createStageDir(target: Target, packager: PlatformPackager<any>, arch: Arch): Promise<StageDir>;
export declare function createStageDirPath(target: Target, packager: PlatformPackager<any>, arch: Arch): Promise<string>;
export declare function getWindowsInstallationDirName(appInfo: AppInfo, isTryToUseProductName: boolean): string;
export declare function getWindowsInstallationAppPackageName(appName: string): string;

View File

@ -0,0 +1,47 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getWindowsInstallationAppPackageName = exports.getWindowsInstallationDirName = exports.createStageDirPath = exports.createStageDir = exports.StageDir = void 0;
const path = require("path");
const builder_util_1 = require("builder-util");
const fs = require("fs/promises");
class StageDir {
constructor(dir) {
this.dir = dir;
}
getTempFile(name) {
return this.dir + path.sep + name;
}
cleanup() {
if (!builder_util_1.debug.enabled || process.env.ELECTRON_BUILDER_REMOVE_STAGE_EVEN_IF_DEBUG === "true") {
return fs.rm(this.dir, { recursive: true, force: true });
}
return Promise.resolve();
}
toString() {
return this.dir;
}
}
exports.StageDir = StageDir;
async function createStageDir(target, packager, arch) {
return new StageDir(await createStageDirPath(target, packager, arch));
}
exports.createStageDir = createStageDir;
async function createStageDirPath(target, packager, arch) {
const tempDir = packager.info.stageDirPathCustomizer(target, packager, arch);
await fs.rm(tempDir, { recursive: true, force: true });
await fs.mkdir(tempDir, { recursive: true });
return tempDir;
}
exports.createStageDirPath = createStageDirPath;
// https://github.com/electron-userland/electron-builder/issues/3100
// https://github.com/electron-userland/electron-builder/commit/2539cfba20dc639128e75c5b786651b652bb4b78
function getWindowsInstallationDirName(appInfo, isTryToUseProductName) {
return isTryToUseProductName && /^[-_+0-9a-zA-Z .]+$/.test(appInfo.productFilename) ? appInfo.productFilename : appInfo.sanitizedName;
}
exports.getWindowsInstallationDirName = getWindowsInstallationDirName;
// https://github.com/electron-userland/electron-builder/issues/6747
function getWindowsInstallationAppPackageName(appName) {
return appName.replace(/\//g, "\\");
}
exports.getWindowsInstallationAppPackageName = getWindowsInstallationAppPackageName;
//# sourceMappingURL=targetUtil.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"targetUtil.js","sourceRoot":"","sources":["../../src/targets/targetUtil.ts"],"names":[],"mappings":";;;AAAA,6BAA4B;AAE5B,+CAA0C;AAE1C,kCAAiC;AAEjC,MAAa,QAAQ;IACnB,YAAqB,GAAW;QAAX,QAAG,GAAH,GAAG,CAAQ;IAAG,CAAC;IAEpC,WAAW,CAAC,IAAY;QACtB,OAAO,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,GAAG,IAAI,CAAA;IACnC,CAAC;IAED,OAAO;QACL,IAAI,CAAC,oBAAK,CAAC,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,2CAA2C,KAAK,MAAM,EAAE,CAAC;YACzF,OAAO,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;QAC1D,CAAC;QACD,OAAO,OAAO,CAAC,OAAO,EAAE,CAAA;IAC1B,CAAC;IAED,QAAQ;QACN,OAAO,IAAI,CAAC,GAAG,CAAA;IACjB,CAAC;CACF;AAjBD,4BAiBC;AAEM,KAAK,UAAU,cAAc,CAAC,MAAc,EAAE,QAA+B,EAAE,IAAU;IAC9F,OAAO,IAAI,QAAQ,CAAC,MAAM,kBAAkB,CAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAA;AACvE,CAAC;AAFD,wCAEC;AAEM,KAAK,UAAU,kBAAkB,CAAC,MAAc,EAAE,QAA+B,EAAE,IAAU;IAClG,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,sBAAsB,CAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAA;IAC5E,MAAM,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;IACtD,MAAM,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IAC5C,OAAO,OAAO,CAAA;AAChB,CAAC;AALD,gDAKC;AAED,oEAAoE;AACpE,wGAAwG;AACxG,SAAgB,6BAA6B,CAAC,OAAgB,EAAE,qBAA8B;IAC5F,OAAO,qBAAqB,IAAI,qBAAqB,CAAC,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC,OAAO,CAAC,aAAa,CAAA;AACvI,CAAC;AAFD,sEAEC;AAED,oEAAoE;AACpE,SAAgB,oCAAoC,CAAC,OAAe;IAClE,OAAO,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;AACrC,CAAC;AAFD,oFAEC","sourcesContent":["import * as path from \"path\"\nimport { Target, AppInfo } from \"../\"\nimport { Arch, debug } from \"builder-util\"\nimport { PlatformPackager } from \"../platformPackager\"\nimport * as fs from \"fs/promises\"\n\nexport class StageDir {\n constructor(readonly dir: string) {}\n\n getTempFile(name: string) {\n return this.dir + path.sep + name\n }\n\n cleanup() {\n if (!debug.enabled || process.env.ELECTRON_BUILDER_REMOVE_STAGE_EVEN_IF_DEBUG === \"true\") {\n return fs.rm(this.dir, { recursive: true, force: true })\n }\n return Promise.resolve()\n }\n\n toString() {\n return this.dir\n }\n}\n\nexport async function createStageDir(target: Target, packager: PlatformPackager<any>, arch: Arch): Promise<StageDir> {\n return new StageDir(await createStageDirPath(target, packager, arch))\n}\n\nexport async function createStageDirPath(target: Target, packager: PlatformPackager<any>, arch: Arch): Promise<string> {\n const tempDir = packager.info.stageDirPathCustomizer(target, packager, arch)\n await fs.rm(tempDir, { recursive: true, force: true })\n await fs.mkdir(tempDir, { recursive: true })\n return tempDir\n}\n\n// https://github.com/electron-userland/electron-builder/issues/3100\n// https://github.com/electron-userland/electron-builder/commit/2539cfba20dc639128e75c5b786651b652bb4b78\nexport function getWindowsInstallationDirName(appInfo: AppInfo, isTryToUseProductName: boolean): string {\n return isTryToUseProductName && /^[-_+0-9a-zA-Z .]+$/.test(appInfo.productFilename) ? appInfo.productFilename : appInfo.sanitizedName\n}\n\n// https://github.com/electron-userland/electron-builder/issues/6747\nexport function getWindowsInstallationAppPackageName(appName: string): string {\n return appName.replace(/\\//g, \"\\\\\")\n}\n"]}

View File

@ -0,0 +1 @@
export declare function getLinuxToolsPath(): Promise<string>;

10
mc_test/node_modules/app-builder-lib/out/targets/tools.js generated vendored Executable file
View File

@ -0,0 +1,10 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getLinuxToolsPath = void 0;
const binDownload_1 = require("../binDownload");
function getLinuxToolsPath() {
//noinspection SpellCheckingInspection
return (0, binDownload_1.getBinFromUrl)("linux-tools", "mac-10.12.3", "SQ8fqIRVXuQVWnVgaMTDWyf2TLAJjJYw3tRSqQJECmgF6qdM7Kogfa6KD49RbGzzMYIFca9Uw3MdsxzOPRWcYw==");
}
exports.getLinuxToolsPath = getLinuxToolsPath;
//# sourceMappingURL=tools.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"tools.js","sourceRoot":"","sources":["../../src/targets/tools.ts"],"names":[],"mappings":";;;AAAA,gDAA8C;AAE9C,SAAgB,iBAAiB;IAC/B,sCAAsC;IACtC,OAAO,IAAA,2BAAa,EAAC,aAAa,EAAE,aAAa,EAAE,0FAA0F,CAAC,CAAA;AAChJ,CAAC;AAHD,8CAGC","sourcesContent":["import { getBinFromUrl } from \"../binDownload\"\n\nexport function getLinuxToolsPath() {\n //noinspection SpellCheckingInspection\n return getBinFromUrl(\"linux-tools\", \"mac-10.12.3\", \"SQ8fqIRVXuQVWnVgaMTDWyf2TLAJjJYw3tRSqQJECmgF6qdM7Kogfa6KD49RbGzzMYIFca9Uw3MdsxzOPRWcYw==\")\n}\n"]}