From e045142a577f26a09251033c5d44390871d5875e Mon Sep 17 00:00:00 2001 From: Black Platypus Date: Mon, 7 Oct 2024 12:33:06 +0200 Subject: [PATCH] Add "prepare" mode --- src/bld.js | 119 +++++++++++++++++++++++++++++++------------------ src/bld/osx.js | 3 +- src/index.d.ts | 2 +- src/index.js | 8 +++- src/util.js | 8 ++-- 5 files changed, 88 insertions(+), 52 deletions(-) diff --git a/src/bld.js b/src/bld.js index d2895854..36e35849 100644 --- a/src/bld.js +++ b/src/bld.js @@ -122,44 +122,65 @@ async function bld({ nativeAddon = false, zip = false, releaseInfo = {}, + mode = 'build' }) { const nwDir = path.resolve( cacheDir, `nwjs${flavor === 'sdk' ? '-sdk' : ''}-v${version}-${platform }-${arch}`, ); - - await fs.promises.rm(outDir, { force: true, recursive: true }); - await fs.promises.cp(nwDir, outDir, { recursive: true, verbatimSymlinks: true }); - - const files = await util.globFiles({ srcDir, glob }); - const manifest = await util.getNodeManifest({ srcDir, glob }); - - if (glob) { - for (let file of files) { + + if(mode == "build"){ + // TODO: Check if outDir is really a previous built dir (known files?) to avoid catastrophy on misconfiguration + await fs.promises.rm(outDir, { force: true, recursive: true }); + await fs.promises.cp(nwDir, outDir, { recursive: true, verbatimSymlinks: true }); + + const files = await util.globFiles({ srcDir, glob }); + const manifest = await util.getNodeManifest({ srcDir, glob }); + + if (glob) { + for (let file of files) { + await fs.promises.cp( + file, + path.resolve( + outDir, + platform !== 'osx' + ? 'package.nw' + : 'nwjs.app/Contents/Resources/app.nw', + file, + ), + { recursive: true, verbatimSymlinks: true }, + ); + } + } else { await fs.promises.cp( - file, + files, path.resolve( outDir, platform !== 'osx' ? 'package.nw' : 'nwjs.app/Contents/Resources/app.nw', - file, ), { recursive: true, verbatimSymlinks: true }, ); } - } else { - await fs.promises.cp( - files, - path.resolve( - outDir, - platform !== 'osx' - ? 'package.nw' - : 'nwjs.app/Contents/Resources/app.nw', - ), - { recursive: true, verbatimSymlinks: true }, - ); + } + + var appInfo = {version, flavor, platform, arch, srcDir, cacheDir, outDir, app, glob, managedManifest, nativeAddon, zip, releaseInfo, mode}; + if (platform === 'linux') { + Object.assign(appInfo, prepareLinuxConfig(appInfo)); + if(mode=='prepare') + return appInfo; + await applyLinuxConfig(appInfo); + } else if (platform === 'win') { + Object.assign(appInfo, prepareWinConfig(appInfo)); + if(mode=='prepare') + return appInfo; + await applyWinConfig(appInfo); + } else if (platform === 'osx') { + Object.assign(appInfo, await setOsxConfig(appInfo)); + if(mode=='prepare') + return appInfo; } const nodeVersion = releaseInfo.components.node; @@ -172,14 +193,6 @@ async function bld({ await manageManifest({ nwPkg: manifest, managedManifest, outDir, platform }); } - if (platform === 'linux') { - await setLinuxConfig({ app, outDir }); - } else if (platform === 'win') { - await setWinConfig({ app, outDir }); - } else if (platform === 'osx') { - await setOsxConfig({ app, outDir, releaseInfo }); - } - if (nativeAddon === 'gyp') { buildNativeAddon({ cacheDir, version, platform, arch, outDir, nodeVersion }); } @@ -187,6 +200,7 @@ async function bld({ if (zip !== false) { await compress({ zip, outDir }); } + return appInfo; } const manageManifest = async ({ nwPkg, managedManifest, outDir, platform }) => { @@ -237,7 +251,7 @@ const manageManifest = async ({ nwPkg, managedManifest, outDir, platform }) => { } }; -const setLinuxConfig = async ({ app, outDir }) => { +const prepareLinuxConfig = async ({ app, outDir }) => { if (process.platform === 'win32') { console.warn( 'Linux apps built on Windows platform do not preserve all file permissions. See #716', @@ -270,7 +284,21 @@ const setLinuxConfig = async ({ app, outDir }) => { SingleMainWindow: app.singleMainWindow, }; - await fs.promises.rename(`${outDir}/nw`, `${outDir}/${app.name}`); + return { + outDir, + executablePath: `${outDir}/${app.name}`, + desktopEntryFile + }; +}; + +const applyLinuxConfig = async ({ outDir, executablePath, desktopEntryFile }) => { + if (process.platform === 'win32') { + console.warn( + 'Linux apps built on Windows platform do not preserve all file permissions. See #716', + ); + } + + await fs.promises.rename(`${outDir}/nw`, executablePath); let fileContent = '[Desktop Entry]\n'; Object.keys(desktopEntryFile).forEach((key) => { @@ -278,12 +306,12 @@ const setLinuxConfig = async ({ app, outDir }) => { fileContent += `${key}=${desktopEntryFile[key]}\n`; } }); - let filePath = `${outDir}/${app.name}.desktop`; + let filePath = `${executablePath}.desktop`; await fs.promises.writeFile(filePath, fileContent); }; -const setWinConfig = async ({ app, outDir }) => { - let versionString = { +const prepareWinConfig = ({ app, outDir }) => { + let versionInfo = { Comments: app.comments, CompanyName: app.company, FileDescription: app.fileDescription, @@ -298,15 +326,18 @@ const setWinConfig = async ({ app, outDir }) => { SpecialBuild: app.specialBuild, }; - Object.keys(versionString).forEach((option) => { - if (versionString[option] === undefined) { - delete versionString[option]; + Object.keys(versionInfo).forEach((option) => { + if (versionInfo[option] === undefined) { + delete versionInfo[option]; } }); - - const outDirAppExe = path.resolve(outDir, `${app.name}.exe`); - await fs.promises.rename(path.resolve(outDir, 'nw.exe'), outDirAppExe); - const exe = peLibrary.NtExecutable.from(await fs.promises.readFile(outDirAppExe)); + const executableName = app.name.replace(/[<>:"/\\|?*\u0000-\u001F]/g, ""); + const executablePath = path.resolve(outDir, `${executableName}.exe`); + return { versionInfo, outDir, executablePath }; +}; +const applyWinConfig = async ({ app, versionInfo, outDir, executablePath }) => { + await fs.promises.rename(path.resolve(outDir, 'nw.exe'), executablePath); + const exe = peLibrary.NtExecutable.from(await fs.promises.readFile(executablePath)); const res = peLibrary.NtExecutableResource.from(exe); // English (United States) const EN_US = 1033; @@ -335,11 +366,11 @@ const setWinConfig = async ({ app, outDir }) => { vi.setStringValues({ lang: app.languageCode, codepage: 1200 - }, versionString); + }, versionInfo); vi.outputToResourceEntries(res.entries); res.outputResource(exe); const outBuffer = Buffer.from(exe.generate()); - await fs.promises.writeFile(outDirAppExe, outBuffer); + await fs.promises.writeFile(executablePath, outBuffer); }; const buildNativeAddon = ({ cacheDir, version, platform, arch, outDir, nodeVersion }) => { diff --git a/src/bld/osx.js b/src/bld/osx.js index 41f01c7f..f30a1db0 100644 --- a/src/bld/osx.js +++ b/src/bld/osx.js @@ -30,7 +30,8 @@ async function updateHelperPlist (plistPath, helperName, helperId, appCFBundleId * @param {string} options.releaseInfo - Release information. * @returns {Promise} - Promise. */ -export default async function setOsxConfig({ app, outDir, releaseInfo }) { +// TODO: implement "prepare" mode +export default async function setOsxConfig({ app, outDir, releaseInfo, mode }) { if (process.platform === 'win32') { console.warn( 'MacOS apps built on Windows platform do not preserve all file permissions. See #716', diff --git a/src/index.d.ts b/src/index.d.ts index 0b686134..4d2ff490 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -8,7 +8,7 @@ export interface Options

{ /** String of space separated glob patterns which correspond to NW app code */ srcDir?: "./" | string, /** Run or build application */ - mode?: "build" | "get" | "run", + mode?: "build" | "get" | "run" | "prepare", /** NW runtime version */ version?: "latest" | "stable" | string, /** NW runtime flavor */ diff --git a/src/index.js b/src/index.js index 2f197dfb..ff01a17d 100644 --- a/src/index.js +++ b/src/index.js @@ -44,6 +44,7 @@ async function nwbuild(options) { try { // Parse options // options = await util.parse(options, manifest); // Preserve user input for now, assign manifest overwrites, only then parse + var isPrepare = options.mode === 'prepare'; manifest = await util.getNodeManifest({ srcDir: options.srcDir, glob: options.glob }); if (typeof manifest?.nwbuild === 'object') { @@ -53,6 +54,8 @@ async function nwbuild(options) { Object.assign(options, manifest.nwbuild); options.app = appOptions; } + if(isPrepare) + options.mode = 'prepare'; options = await util.parse(options, manifest); @@ -113,8 +116,9 @@ async function nwbuild(options) { glob: options.glob, argv: options.argv, }); - } else if (options.mode === 'build') { - await bld({ + } else if (options.mode === 'build' || options.mode === 'prepare') { + return await bld({ + mode: options.mode, version: options.version, flavor: options.flavor, platform: options.platform, diff --git a/src/util.js b/src/util.js index e8a8319e..f3eb93a0 100644 --- a/src/util.js +++ b/src/util.js @@ -202,7 +202,8 @@ export const parse = async (options, pkg) => { options.app = options.app ?? {}; options.app.name = options.app.name ?? pkg.name; - options.app.icon = options.app.icon ?? undefined; + options.app.name = options.app.name.replace(/[<>:"/\\|?*\u0000-\u001F]/g, ""); + options.app.icon = options.app.icon ? path.resolve(options.app.icon) : undefined; // TODO(#737): move this out if (options.platform === 'linux') { @@ -278,10 +279,9 @@ export const parse = async (options, pkg) => { * @throws {Error} Throw error if options are invalid */ export const validate = async (options, releaseInfo) => { - if (!['get', 'run', 'build'].includes(options.mode)) { + if (!['get', 'run', 'build', 'prepare'].includes(options.mode)) { throw new Error( - `Unknown mode ${options.mode}. Expected "get", "run" or "build".`, - ); + `Unknown mode ${options.mode}. Expected "get", "run", "build" or "prepare".`); } if (typeof releaseInfo === 'undefined') { throw new Error(