UNPKG

vrrv-installer-builder

Version:

A complete solution to package and build a ready for distribution Electron app for MacOS, Windows and Linux with “auto update” support out of the box

231 lines (227 loc) 13.7 kB
"use strict"; var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); const metadata_1 = require("../metadata"); const util_1 = require("../util/util"); const path = require("path"); const bluebird_1 = require("bluebird"); const binDownload_1 = require("../util/binDownload"); const uuid_1345_1 = require("uuid-1345"); const platformPackager_1 = require("../platformPackager"); const archive_1 = require("./archive"); const log_1 = require("../util/log"); const fs_extra_p_1 = require("fs-extra-p"); const semver = require("semver"); //noinspection JSUnusedLocalSymbols const __awaiter = require("../util/awaiter"); const NSIS_VERSION = "3.0.1"; //noinspection SpellCheckingInspection const NSIS_SHA2 = "23280f66c07c923da6f29a3c318377720c8ecd7af4de3755256d1ecf60d07f74"; //noinspection SpellCheckingInspection const ELECTRON_BUILDER_NS_UUID = "50e065bc-3134-11e6-9bab-38c9862bdaf3"; const nsisPathPromise = binDownload_1.getBinFromBintray("nsis", NSIS_VERSION, NSIS_SHA2); class NsisTarget extends platformPackager_1.Target { constructor(packager, outDir) { super("nsis"); this.packager = packager; this.outDir = outDir; this.archs = new Map(); this.nsisTemplatesDir = path.join(__dirname, "..", "..", "templates", "nsis"); this.options = packager.info.devMetadata.build.nsis || Object.create(null); // CFBundleTypeName // https://developer.apple.com/library/ios/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html#//apple_ref/doc/uid/20001431-101685 // CFBundleTypeExtensions } build(arch, appOutDir) { return __awaiter(this, void 0, void 0, function* () { const packager = this.packager; const archSuffix = metadata_1.Arch[arch]; const archiveFile = path.join(this.outDir, `${ packager.appInfo.name }-${ packager.appInfo.version }-${ archSuffix }.nsis.7z`); this.archs.set(arch, log_1.task(`Creating NSIS ${ archSuffix } package`, archive_1.archiveApp(packager.devMetadata.build.compression, "7z", archiveFile, appOutDir, true))); }); } finishBuild() { return log_1.task("Building NSIS installer", this.buildInstaller().then(() => bluebird_1.Promise.map(this.archs.values(), it => fs_extra_p_1.unlink(it)))); } buildInstaller() { return __awaiter(this, void 0, void 0, function* () { const packager = this.packager; const iconPath = yield packager.getIconPath(); const appInfo = packager.appInfo; const version = appInfo.version; const installerPath = path.join(this.outDir, `${ appInfo.productFilename } Setup ${ version }.exe`); const guid = this.options.guid || (yield bluebird_1.Promise.promisify(uuid_1345_1.v5)({ namespace: ELECTRON_BUILDER_NS_UUID, name: appInfo.id })); const defines = { APP_ID: appInfo.id, APP_GUID: guid, PRODUCT_NAME: appInfo.productName, PRODUCT_FILENAME: appInfo.productFilename, APP_DESCRIPTION: appInfo.description, VERSION: version, COMPANY_NAME: appInfo.companyName, PROJECT_DIR: this.packager.projectDir, BUILD_RESOURCES_DIR: this.packager.buildResourcesDir }; if (iconPath != null) { defines.MUI_ICON = iconPath; defines.MUI_UNICON = iconPath; } for (let _ref of this.archs) { var _ref2 = _slicedToArray(_ref, 2); let arch = _ref2[0]; let file = _ref2[1]; defines[arch === metadata_1.Arch.x64 ? "APP_64" : "APP_32"] = yield file; } const oneClick = this.options.oneClick !== false; const installerHeader = oneClick ? null : yield this.packager.getResource(this.options.installerHeader, "installerHeader.bmp"); if (installerHeader != null) { defines.MUI_HEADERIMAGE = null; defines.MUI_HEADERIMAGE_RIGHT = null; defines.MUI_HEADERIMAGE_BITMAP = installerHeader; } const installerHeaderIcon = oneClick ? yield this.packager.getResource(this.options.installerHeaderIcon, "installerHeaderIcon.ico") : null; if (installerHeaderIcon != null) { defines.HEADER_ICO = installerHeaderIcon; } if (this.options.perMachine === true) { defines.INSTALL_MODE_PER_ALL_USERS = null; } if (!oneClick || this.options.perMachine === true) { defines.INSTALL_MODE_PER_ALL_USERS_REQUIRED = null; } if (oneClick) { if (this.options.runAfterFinish !== false) { defines.RUN_AFTER_FINISH = null; } } else if (this.options.allowElevation !== false) { defines.MULTIUSER_INSTALLMODE_ALLOW_ELEVATION = null; } // Error: invalid VIProductVersion format, should be X.X.X.X // so, we must strip beta const parsedVersion = new semver.SemVer(appInfo.version); const localeId = this.options.language || "1033"; const versionKey = [`/LANG=${ localeId } ProductName "${ appInfo.productName }"`, `/LANG=${ localeId } ProductVersion "${ appInfo.version }"`, `/LANG=${ localeId } CompanyName "${ appInfo.companyName }"`, `/LANG=${ localeId } LegalCopyright "${ appInfo.copyright }"`, `/LANG=${ localeId } FileDescription "${ appInfo.description }"`, `/LANG=${ localeId } FileVersion "${ appInfo.buildVersion }"`]; util_1.use(this.packager.platformSpecificBuildOptions.legalTrademarks, it => versionKey.push(`/LANG=${ localeId } LegalTrademarks "${ it }"`)); const commands = { OutFile: `"${ installerPath }"`, VIProductVersion: `${ parsedVersion.major }.${ parsedVersion.minor }.${ parsedVersion.patch }.${ appInfo.buildNumber || "0" }`, VIAddVersionKey: versionKey }; if (packager.devMetadata.build.compression === "store") { commands.SetCompress = "off"; defines.COMPRESS = "off"; } else { commands.SetCompressor = "lzma"; // default is 8: test app installer size 37.2 vs 36 if dict size 64 commands.SetCompressorDictSize = "64"; defines.COMPRESS = "auto"; } if (oneClick) { defines.ONE_CLICK = null; } util_1.debug(defines); util_1.debug(commands); if (this.packager.options.effectiveOptionComputed != null && this.packager.options.effectiveOptionComputed([defines, commands])) { return; } const customScriptPath = yield this.packager.getResource(this.options.script, "installer.nsi"); const script = yield fs_extra_p_1.readFile(customScriptPath || path.join(this.nsisTemplatesDir, "installer.nsi"), "utf8"); if (customScriptPath == null) { const uninstallerPath = yield packager.getTempFile("uninstaller.exe"); const isWin = process.platform === "win32"; defines.BUILD_UNINSTALLER = null; defines.UNINSTALLER_OUT_FILE = isWin ? uninstallerPath : path.win32.join("Z:", uninstallerPath); yield log_1.subTask(`Executing makensis — uninstaller`, this.executeMakensis(defines, commands, false, script)); yield util_1.exec(isWin ? installerPath : "wine", isWin ? [] : [installerPath]); yield packager.sign(uninstallerPath); delete defines.BUILD_UNINSTALLER; // platform-specific path, not wine defines.UNINSTALLER_OUT_FILE = uninstallerPath; } else { log_1.log("Custom NSIS script is used - uninstaller is not signed by electron-builder"); } yield log_1.subTask(`Executing makensis — installer`, this.executeMakensis(defines, commands, true, script)); yield packager.sign(installerPath); this.packager.dispatchArtifactCreated(installerPath, `${ appInfo.name }-Setup-${ version }.exe`); }); } executeMakensis(defines, commands, isInstaller, originalScript) { return __awaiter(this, void 0, void 0, function* () { const args = this.options.warningsAsErrors === false ? [] : ["-WX"]; for (let name of Object.keys(defines)) { const value = defines[name]; if (value == null) { args.push(`-D${ name }`); } else { args.push(`-D${ name }=${ value }`); } } for (let name of Object.keys(commands)) { const value = commands[name]; if (Array.isArray(value)) { for (let c of value) { args.push(`-X${ name } ${ c }`); } } else { args.push(`-X${ name } ${ value }`); } } args.push("-"); const binDir = process.platform === "darwin" ? "mac" : process.platform === "win32" ? "Bin" : "linux"; const nsisPath = yield nsisPathPromise; let script = originalScript; const packager = this.packager; const customInclude = yield packager.getResource(this.options.include, "installer.nsh"); if (customInclude != null) { script = `!include "${ customInclude }"\n!addincludedir "${ packager.buildResourcesDir }"\n${ script }`; } const fileAssociations = packager.getFileAssociations(); if (fileAssociations.length !== 0) { script = "!include FileAssociation.nsh\n" + script; if (isInstaller) { let registerFileAssociationsScript = ""; for (let item of fileAssociations) { const extensions = util_1.asArray(item.ext).map(platformPackager_1.normalizeExt); for (let ext of extensions) { const customIcon = yield packager.getResource(item.icon, `${ extensions[0] }.ico`); let installedIconPath = "$appExe,0"; if (customIcon != null) { installedIconPath = `$INSTDIR\\resources\\${ path.basename(customIcon) }`; //noinspection SpellCheckingInspection registerFileAssociationsScript += ` File "/oname=${ installedIconPath }" "${ customIcon }"\n`; } const icon = `"${ installedIconPath }"`; const commandText = `"Open with ${ packager.appInfo.productName }"`; const command = '"$appExe $\\"%1$\\""'; registerFileAssociationsScript += ` !insertmacro APP_ASSOCIATE "${ ext }" "${ item.name }" "${ item.description || "" }" ${ icon } ${ commandText } ${ command }\n`; } } script = `!macro registerFileAssociations\n${ registerFileAssociationsScript }!macroend\n${ script }`; } else { let unregisterFileAssociationsScript = ""; for (let item of fileAssociations) { for (let ext of util_1.asArray(item.ext)) { unregisterFileAssociationsScript += ` !insertmacro APP_UNASSOCIATE "${ platformPackager_1.normalizeExt(ext) }" "${ item.name }"\n`; } } script = `!macro unregisterFileAssociations\n${ unregisterFileAssociationsScript }!macroend\n${ script }`; } } if (util_1.debug.enabled) { process.stdout.write("\n\nNSIS script:\n\n" + script + "\n\n---\nEnd of NSIS script.\n\n"); } yield new bluebird_1.Promise((resolve, reject) => { const command = path.join(nsisPath, binDir, process.platform === "win32" ? "makensis.exe" : "makensis"); const childProcess = util_1.doSpawn(command, args, { // 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: Object.assign({}, process.env, { NSISDIR: nsisPath }), cwd: this.nsisTemplatesDir }, true); util_1.handleProcess("close", childProcess, command, resolve, reject); childProcess.stdin.end(script); }); }); } } Object.defineProperty(exports, "__esModule", { value: true }); exports.default = NsisTarget; //# sourceMappingURL=nsis.js.map