UNPKG

nw-builder

Version:

Build NW.js desktop applications for MacOS, Windows and Linux.

223 lines (200 loc) 7.1 kB
import console from "node:console"; import fs from "node:fs"; import path from "node:path"; import process from "node:process"; import { build, parse } from "plist"; import semver from "semver"; /** * Function to update Helper App Plist Files * @param {string} plistPath - Path to Helper App Plist File * @param {string} helperName - Helper App Name * @param {string} helperId - Helper App ID * @param {string} appCFBundleIdentifier - options.app.CFBundleIdentifier */ async function updateHelperPlist( plistPath, helperName, helperId, appCFBundleIdentifier, ) { const plistFullPath = path.resolve(plistPath, "Contents/Info.plist"); const plistJson = parse(await fs.promises.readFile(plistFullPath, "utf-8")); plistJson.CFBundleDisplayName = helperName; plistJson.CFBundleName = helperName; plistJson.CFBundleExecutable = helperName; plistJson.CFBundleIdentifier = `${appCFBundleIdentifier}.${helperId}`; await fs.promises.writeFile(plistFullPath, build(plistJson)); } /** * * @param {object} options - Options. * @param {string} options.version - NW.js version. * @param {object} options.app - Application configuration. * @param {string} options.outDir - Output directory. * @param {string} options.releaseInfo - Release information. * @returns {Promise<void>} - Promise. */ export default async function setOsxConfig({ version, app, outDir, releaseInfo, }) { if (process.platform === "win32") { console.warn( "MacOS apps built on Windows platform do not preserve all file permissions. See https://github.com/nwutils/nw-builder/issues/716", ); } try { /** * @type {string | null} */ const chromiumVersion = releaseInfo?.components?.chromium; if (!chromiumVersion) { throw new Error("Chromium version is missing."); } /** * Path to MacOS application. * @type {string} */ const nwjsApp = path.resolve(outDir, "nwjs.app"); /** * Path to renamed MacOS application. * @type {string} */ const outApp = path.resolve(outDir, `${app.name}.app`); /* Rename `nwjs.app` to `${app.name}.app` */ await fs.promises.rename(nwjsApp, outApp); /* Rename `Contents/MacOS/nwjs` to `Contents/MacOS/${app.name}` */ await fs.promises.rename( path.resolve(outApp, "Contents", "MacOS", "nwjs"), path.resolve(outApp, "Contents", "MacOS", app.name), ); /* Rename all Helper apps */ const helperBaseDir = path.resolve( outApp, "Contents/Frameworks/nwjs Framework.framework/Versions", /* TODO: Version mismatch tracked in https://github.com/nwjs/nw.js/issues/8371 */ "Current", "Helpers", ); const helperApps = [ { name: "nwjs Helper (Alerts).app", id: "helper.alert" }, { name: "nwjs Helper (GPU).app", id: "helper.gpu" }, { name: "nwjs Helper (Renderer).app", id: "helper.renderer" }, { name: "nwjs Helper.app", id: "helper" }, ]; /* MacOS Plugin Helper is removed in NW.js v0.111.0 (Chromium M148) */ if (semver.lt(version, "0.111.0")) { helperApps.push({ name: "nwjs Helper (Plugin).app", id: "helper.plugin", }); } for (const helperApp of helperApps) { const newHelperAppName = helperApp.name.replace(/^nwjs/, app.name); const oldPath = path.resolve(helperBaseDir, helperApp.name); const newPath = path.resolve(helperBaseDir, newHelperAppName); // Rename Helper base directory await fs.promises.rename(oldPath, newPath); // Rename Helper sub-directory const helperBaseName = helperApp.name.replace(/.app$/, ""); const subPathBase = path.resolve(newPath, "Contents/MacOS/"); const oldSubPath = path.resolve(subPathBase, helperBaseName); const newSubPath = path.resolve( subPathBase, helperBaseName.replace(/^nwjs/, app.name), ); await fs.promises.rename(oldSubPath, newSubPath); // Update Helper Plist file await updateHelperPlist( newPath, newHelperAppName.replace(/.app$/, ""), helperApp.id, app.CFBundleIdentifier, ); } /* Replace default icon with user defined icon if specified. */ if (app.icon !== undefined) { await fs.promises.copyFile( path.resolve(app.icon), path.resolve(outApp, "Contents", "Resources", "app.icns"), ); } /** * Path to `nwjs.app/Contents/Info.plist` * @type {string} */ const contentsInfoPlistPath = path.resolve( outApp, "Contents", "Info.plist", ); /** * Path to `nwjs.app/Contents/Resources/en.lproj/InfoPlist.settings` * @type {string} */ const contentsResourcesEnLprojInfoPlistStringsPath = path.resolve( outApp, "Contents", "Resources", "en.lproj", "InfoPlist.strings", ); /** * JSON from `nwjs.app/Contents/Info.plist` * @type {object} */ const contentsInfoPlistJson = parse( await fs.promises.readFile(contentsInfoPlistPath, "utf-8"), ); /* Update Plist with user defined values. */ contentsInfoPlistJson.LSApplicationCategoryType = app.LSApplicationCategoryType; contentsInfoPlistJson.CFBundleIdentifier = app.CFBundleIdentifier; contentsInfoPlistJson.CFBundleName = app.CFBundleName; contentsInfoPlistJson.CFBundleDisplayName = app.CFBundleDisplayName; contentsInfoPlistJson.CFBundleSpokenName = app.CFBundleSpokenName; contentsInfoPlistJson.CFBundleVersion = app.CFBundleVersion; contentsInfoPlistJson.CFBundleShortVersionString = app.CFBundleShortVersionString; contentsInfoPlistJson.CFBundleExecutable = app.name; contentsInfoPlistJson.NSLocalNetworkUsageDescription = app.NSLocalNetworkUsageDescription; /* Remove properties that were not updated by the user. */ Object.keys(contentsInfoPlistJson).forEach((option) => { if (contentsInfoPlistJson[option] === undefined) { delete contentsInfoPlistJson[option]; } }); /** * Data from `nwjs.app/Contents/Resources/en.lproj/InfoPlist.settings` * @type {string[]} */ const contentsResourcesEnLprojInfoPlistStringsArray = ( await fs.promises.readFile( contentsResourcesEnLprojInfoPlistStringsPath, "utf-8", ) ).split("\n"); contentsResourcesEnLprojInfoPlistStringsArray.forEach((line, idx, arr) => { if (line.includes("NSHumanReadableCopyright")) { arr[idx] = `NSHumanReadableCopyright = "${app.NSHumanReadableCopyright}";`; } }); /* Write the updated values to their config files. */ await fs.promises.writeFile( contentsInfoPlistPath, build(contentsInfoPlistJson), ); await fs.promises.writeFile( contentsResourcesEnLprojInfoPlistStringsPath, contentsResourcesEnLprojInfoPlistStringsArray .toString() .replace(/,/g, "\n"), ); } catch (error) { console.error(error); } }