Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(build): add managed manifest #965

Merged
merged 12 commits into from
Oct 18, 2023
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]

## [4.4.2-beta.1] - 2023-10-16

### Added

- Managed Manifest mode. `options.ManagedManifest` defaults to `false`.
- If `true`, then first `package.json` globbed is parsed as manifest.
- If JSON type, object is parsed as manifest.
- If string type, then resolve as file path to manifest.

## [4.4.2] - 2023-10-16

### Changed
Expand Down
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "nw-builder",
"version": "4.4.2",
"version": "4.4.2-beta.1",
"description": "Build NW.js desktop applications for MacOS, Windows and Linux.",
"keywords": [
"NW.js",
Expand Down Expand Up @@ -70,8 +70,8 @@
"winston": "^3.11.0",
"yargs": "^17.7.2"
},
"packageManager": "npm@9.8.1",
"packageManager": "npm@10.2.0",
"engines": {
"node": "^16.20.1 || ^18.18.2 || >= 20.8.1"
"node": ">=14"
}
}
125 changes: 116 additions & 9 deletions src/build.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { resolve } from "node:path";
import { platform as PLATFORM } from "node:process";
import { platform as PLATFORM, chdir } from "node:process";
import {
cp,
copyFile,
Expand All @@ -14,7 +14,7 @@ import rcedit from "rcedit";
import plist from "plist";

import { log } from "./log.js";

import { exec } from "node:child_process";
/**
* References:
* https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html
Expand Down Expand Up @@ -105,15 +105,60 @@ import { log } from "./log.js";
* mode: "build",
* });
*
* @param {string | string[]} files Array of NW app files
* @param {string} nwDir Directory to hold NW binaries
* @param {string} outDir Directory to store build artifacts
* @param {"linux" | "osx" | "win"} platform Platform is the operating system type
* @param {"zip" | boolean} zip Specify if the build artifacts are to be zipped
* @param {LinuxRc | OsxRc | WinRc} app Multi platform configuration options
* @example
* // Managed Manifest mode
* nwbuild({
* mode: "build",
* managedManifest: true
* });
*
* This will parse the first `package.json` it encounters as the NW.js manifest.
* This is a good way to quickly bootstrap a web/node application as a NW.js application.
* It will remove any development dependencies, autodetect the package manager via `packageManager` and download the relevant dependencies.
*
* @example
* // Managed Manifest JSON
* nwbuild({
* mode: "build",
* managedManifest: { name: "demo", "main": "index.html" }
* });
*
* This will ignore the first `package.json` it encounters and use the user input JSON instead.
* This is good way to customise your existing NW.js application after bootstrapping it.
* It will remove any development dependencies, autodetect the package manager via `packageManager` and download the relevant dependencies.
*
* @example
* // Managed Manifest File
* nwbuild({
* mode: "build",
* managedManifest: "./manifest.json"
* });
*
* Similar to Managed Manifest JSON, this will also ignore the first `package.json` it encounters and use the manifest file provided by the user instead.
* This is good way to customise your existing NW.js application after bootstrapping it. Using JSON vs file is matter of preference.
* It will remove any development dependencies, autodetect the package manager via `packageManager` and download the relevant dependencies.
*
*
* @param {string | string[]} files Array of NW app files
* @param {string} nwDir Directory to hold NW binaries
* @param {string} outDir Directory to store build artifacts
* @param {"linux" | "osx" | "win"} platform Platform is the operating system type
* @param {"zip" | boolean} zip Specify if the build artifacts are to be zipped
* @param {boolean | string | object} managedManifest Managed Manifest mode
* @param {string} nwPkg NW.js manifest file
* @param {LinuxRc | OsxRc | WinRc} app Multi platform configuration options
* @return {Promise<undefined>}
*/
export async function build(files, nwDir, outDir, platform, zip, app) {
export async function build(
files,
nwDir,
outDir,
platform,
zip,
managedManifest,
nwPkg,
app,
) {
log.debug(`Remove any files at ${outDir} directory`);
await rm(outDir, { force: true, recursive: true });
log.debug(`Copy ${nwDir} files to ${outDir} directory`);
Expand Down Expand Up @@ -149,6 +194,68 @@ export async function build(files, nwDir, outDir, platform, zip, app) {
}
}

let manifest = undefined;

if (
typeof managedManifest === "boolean" ||
typeof managedManifest === "object" ||
typeof managedManifest === "string"
) {
if (managedManifest === true) {
log.debug("Enable Managed Manifest Mode.");
manifest = nwPkg;
}

if (typeof managedManifest === "object") {
log.debug("Enable Managed Manifest JSON.");
manifest = managedManifest;
}

if (typeof managedManifest === "string") {
log.debug("Enable Managed Manifest File.");
manifest = JSON.parse(await readFile(managedManifest));
}

log.debug("Remove development dependencies.");
manifest.devDependencies = undefined;
log.debug("Detect Node package manager.");
manifest.packageManager = manifest.packageManager ?? "npm@*";

log.debug(`Write NW.js manifest file to ${outDir} directory`);
await writeFile(
resolve(
outDir,
platform !== "osx"
? "package.nw"
: "nwjs.app/Contents/Resources/app.nw",
"package.json",
),
JSON.stringify(manifest, null, 2),
"utf8",
);

log.debug("Change directory into NW.js application.");
chdir(
resolve(
outDir,
platform !== "osx"
? "package.nw"
: "nwjs.app/Contents/Resources/app.nw",
),
);

if (manifest.packageManager.startsWith("npm")) {
log.debug("Install Node modules via npm.");
exec(`npm install`);
} else if (manifest.packageManager.startsWith("yarn")) {
log.debug("Install Node modules via yarn.");
exec(`yarn install`);
} else if (manifest.packageManager.startsWith("pnpm")) {
log.debug("Install Node modules via pnpm.");
exec(`pnpm install`);
}
}

log.debug(`Starting platform specific config steps for ${platform}`);

if (platform === "linux") {
Expand Down
2 changes: 2 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,8 @@ const nwbuild = async (options) => {
options.outDir,
options.platform,
options.zip,
options.managedManifest,
manifest,
options.app,
);
}
Expand Down
2 changes: 2 additions & 0 deletions src/util/parse.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ export const parse = async (options, pkg) => {
options.outDir = resolve(options.outDir ?? "./out");
options.zip = options.zip ?? false;

options.managedManifest = options.managedManifest ?? false;

options.app = options.app ?? {};
options.app.name = options.app.name ?? pkg.name;
options.app.icon = options.app.icon ?? undefined;
Expand Down
20 changes: 20 additions & 0 deletions src/util/validate.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,26 @@ export const validate = async (options, releaseInfo) => {
await readdir(options.outDir);
}

if (
typeof options.managedManifest !== "boolean" &&
typeof options.managedManifest !== "object" &&
typeof options.managedManifest !== "string"
) {
return new Error(
"Expected options.managedManifest to be a boolean, object or string. Got " +
typeof options.managedManifest,
);
}

if (typeof options.managedManifest === "object") {
if (options.managedManifest.name === undefined) {
return new Error("Expected NW.js Manifest to have a `name` property.");
}
if (options.managedManifest.main === undefined) {
return new Error("Expected NW.js Manifest to have a `main` property.");
}
}

// TODO: Validate app options
return undefined;
};
15 changes: 2 additions & 13 deletions test/fixture/demo.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,8 @@ import nwbuild from "../../src/index.js";

await nwbuild({
srcDir: "app",
mode: "get",
version: "stable",
flavor: "normal",
platform: "linux",
arch: "x64",
cache: true,
ffmpeg: true,
mode: "build",
glob: false,
app: {
company: "Some Company",
fileDescription: "Process Name",
productName: "Some Product",
legalCopyright: "Copyright (c) 2023",
},
logLevel: "debug",
managedManifest: true,
});