nw-builder 4.19.2 → 4.21.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -98,7 +98,7 @@ See `nw-builder` in action by building the demo application.
98
98
 
99
99
  ## Concepts
100
100
 
101
- `nw-builder` can get, run and build NW.js applications. We refer to them as get, run and build modes.
101
+ `nw-builder` can get, run, build and package NW.js applications. We refer to them as get, run, build and package modes.
102
102
 
103
103
  ### Get Mode
104
104
 
@@ -184,32 +184,103 @@ nwbuild({
184
184
  });
185
185
  ```
186
186
 
187
+ ### Package Mode
188
+
189
+ Builds the application, then packages the `outDir` produced by the build via
190
+ [`@nwutils/packager`](https://www.npmjs.com/package/@nwutils/packager). `mode: "package"` resolves with the path to the resulting packaged artifact instead of `undefined`.
191
+
192
+ ```javascript
193
+ const packagePath = await nwbuild({
194
+ mode: "package",
195
+ });
196
+ ```
197
+
198
+ `format` selects the packager to run and defaults to `"AppImage"` on Linux. Only `"AppImage"` is implemented today - `deb`, `rpm`, `msix`, `nsis` and `dmg` are reserved for later.
199
+
200
+ ```javascript
201
+ const appImagePath = await nwbuild({
202
+ mode: "package",
203
+ format: "AppImage",
204
+ });
205
+ ```
206
+
207
+ #### Signing
208
+
209
+ Set `sign` to embed a GPG signature in the AppImage. It needs `gpg` on the host
210
+ with the secret key imported. `signKey` picks the key, defaulting to `gpg`'s
211
+ default secret key. A passphrase protected key reads its passphrase from the
212
+ `APPIMAGETOOL_SIGN_PASSPHRASE` environment variable.
213
+
214
+ ```javascript
215
+ await nwbuild({
216
+ mode: "package",
217
+ sign: true,
218
+ signKey: "ABCDEF1234567890",
219
+ });
220
+ ```
221
+
222
+ #### Self updating
223
+
224
+ Set `publish` to where releases are uploaded to make the AppImage self updatable
225
+ with [`@nwutils/updater`](https://www.npmjs.com/package/@nwutils/updater). An
226
+ `app-update.yml` pointing at `publish` is embedded in the AppImage, and a
227
+ `latest-linux.yml` update info file holding the app's `package.json` `version`
228
+ is written next to it. Upload both to every release.
229
+
230
+ Set `updateInformation: true` to also support zsync based updaters such as
231
+ AppImageUpdate - this needs `zsyncmake` on the host, and the `.zsync` file
232
+ written next to the AppImage must be uploaded too.
233
+
234
+ ```json
235
+ {
236
+ "name": "demo",
237
+ "version": "1.2.0",
238
+ "nwbuild": {
239
+ "mode": "package",
240
+ "platform": "linux",
241
+ "app": { "name": "Demo" },
242
+ "publish": { "provider": "github", "owner": "nwutils", "repo": "demo" },
243
+ "updateInformation": true
244
+ }
245
+ }
246
+ ```
247
+
248
+ With the CLI, pass `--publish.provider=github --publish.owner=nwutils
249
+ --publish.repo=demo`. See the
250
+ [`@nwutils/packager` docs](https://www.npmjs.com/package/@nwutils/packager) for
251
+ the `generic` provider and more details.
252
+
187
253
  ## API Reference
188
254
 
189
255
  Options
190
256
 
191
- | Name | Type | Default | Description |
192
- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
193
- | mode | `"get" \| "run" \| "build"` | `"build"` | Choose between get, run or build mode |
194
- | version | `string \| "latest" \| "stable"` | `"latest"` | Runtime version |
195
- | flavor | `"normal" \| "sdk"` | `"normal"` | Runtime flavor |
196
- | platform | `"linux" \| "osx" \| "win"` | | Host platform |
197
- | arch | `"ia32" \| "x64" \| "arm64"` | | Host architecture |
198
- | downloadUrl | `"https://dl.nwjs.io" \| "https://npm.taobao.org/mirrors/nwjs" \| https://npmmirror.com/mirrors/nwjs \| "https://github.com/corwin-of-amber/nw.js/releases/"` | `"https://dl.nwjs.io"` | Download server. Supports file systems too (for example `file:///home/localghost/nwjs_mirror`) |
199
- | manifestUrl | `"https://nwjs.io/versions.json" \| "https://raw.githubusercontent.com/nwutils/nw-builder/main/src/util/osx.arm.versions.json"` | `"https://nwjs.io/versions.json"` | Versions manifest URI, gotten via https or local file |
200
- | cacheDir | `string` | `"./cache"` | Directory to cache NW binaries |
201
- | cache | `boolean` | `true` | If true the existing cache is used. Otherwise it removes and redownloads it. |
202
- | ffmpeg | `boolean` | `false` | If true the chromium ffmpeg is replaced by community version with proprietary codecs. |
203
- | logLevel | `"error" \| "warn" \| "info" \| "debug"` | `"info"` | Specify level of logging. |
204
- | shaSum | `boolean` | `true` | Flag to enable/disable shasum checks. |
205
- | srcDir | `string` | `"./"` | File paths to application code |
206
- | argv | `string[]` | `[]` | Command line arguments to pass to NW executable in run mode. You can also define these in `chromium-args` in NW.js manifest. |
207
- | glob | `boolean` | `true` | If true file globbing is enabled when parsing `srcDir`. |
208
- | outDir | `string` | `"./out"` | Directory to store build artifacts |
209
- | managedManifest | `boolean \| string \| object` | `false` | Managed manifest |
210
- | nodeAddon | `boolean` | `false` | Rebuild Node native addons |
211
- | zip | `boolean \| "zip" \| "tar" \| "tgz"` | `false` | If true, "zip", "tar" or "tgz" the `outDir` directory is compressed. |
212
- | app | `LinuxRc \| WinRc \| OsxRc` | Additional options for each platform. (See below.) |
257
+ | Name | Type | Default | Description |
258
+ | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
259
+ | mode | `"get" \| "run" \| "build" \| "package"` | `"build"` | Choose between get, run, build or package mode |
260
+ | version | `string \| "latest" \| "stable"` | `"latest"` | Runtime version |
261
+ | flavor | `"normal" \| "sdk"` | `"normal"` | Runtime flavor |
262
+ | platform | `"linux" \| "osx" \| "win"` | | Host platform |
263
+ | arch | `"ia32" \| "x64" \| "arm64"` | | Host architecture |
264
+ | downloadUrl | `"https://dl.nwjs.io" \| "https://npm.taobao.org/mirrors/nwjs" \| https://npmmirror.com/mirrors/nwjs \| "https://github.com/corwin-of-amber/nw.js/releases/"` | `"https://dl.nwjs.io"` | Download server. Supports file systems too (for example `file:///home/localghost/nwjs_mirror`) |
265
+ | manifestUrl | `"https://nwjs.io/versions.json" \| "https://raw.githubusercontent.com/nwutils/nw-builder/main/src/util/osx.arm.versions.json"` | `"https://nwjs.io/versions.json"` | Versions manifest URI, gotten via https or local file |
266
+ | cacheDir | `string` | `"./cache"` | Directory to cache NW binaries |
267
+ | cache | `boolean` | `true` | If true the existing cache is used. Otherwise it removes and redownloads it. |
268
+ | ffmpeg | `boolean` | `false` | If true the chromium ffmpeg is replaced by community version with proprietary codecs. |
269
+ | logLevel | `"error" \| "warn" \| "info" \| "debug"` | `"info"` | Specify level of logging. |
270
+ | shaSum | `boolean` | `true` | Flag to enable/disable shasum checks. |
271
+ | srcDir | `string` | `"./"` | File paths to application code |
272
+ | argv | `string[]` | `[]` | Command line arguments to pass to NW executable in run mode. You can also define these in `chromium-args` in NW.js manifest. |
273
+ | glob | `boolean` | `true` | If true file globbing is enabled when parsing `srcDir`. |
274
+ | outDir | `string` | `"./out"` | Directory to store build artifacts |
275
+ | managedManifest | `boolean \| string \| object` | `false` | Managed manifest |
276
+ | nodeAddon | `boolean` | `false` | Rebuild Node native addons |
277
+ | zip | `boolean \| "zip" \| "tar" \| "tgz"` | `false` | If true, "zip", "tar" or "tgz" the `outDir` directory is compressed. |
278
+ | format | `"AppImage" \| "deb" \| "rpm" \| "msix" \| "nsis" \| "dmg"` | `"AppImage"` on Linux | Packaged output format, used in package mode. Only `"AppImage"` is implemented today. |
279
+ | sign | `boolean` | `false` | If true, embed a GPG signature in the AppImage. Used in package mode. |
280
+ | signKey | `string` | | ID of the GPG key to sign with. Requires `sign`. Used in package mode. |
281
+ | publish | `{provider: "github", owner, repo} \| {provider: "generic", url}` | | Where releases are published. Makes the AppImage self updatable with `@nwutils/updater`. Used in package mode. |
282
+ | updateInformation | `boolean \| string` | | AppImage zsync update information. `true` derives it from `publish`. Used in package mode. |
283
+ | app | `LinuxRc \| WinRc \| OsxRc` | Additional options for each platform. (See below.) |
213
284
 
214
285
  ### `app` configuration object
215
286
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nw-builder",
3
- "version": "4.19.2",
3
+ "version": "4.21.4",
4
4
  "description": "Build NW.js desktop applications for MacOS, Windows and Linux.",
5
5
  "keywords": [
6
6
  "NW.js",
@@ -42,7 +42,6 @@
42
42
  "directory": "./packages/nw-builder"
43
43
  },
44
44
  "scripts": {
45
- "postinstall": "node ./src/postinstall.js",
46
45
  "lint": "eslint ./src ../../tests/specs/nw-builder",
47
46
  "lint:fix": "eslint --fix ./src ../../tests/specs/nw-builder",
48
47
  "type": "tsc",
@@ -55,15 +54,24 @@
55
54
  "demo:exe:win": "../../tests/fixtures/nw-builder/out/win/Demo.exe",
56
55
  "demo:cli": "nwbuild --mode=build --flavor=sdk --glob=false --cacheDir=./node_modules/nw --logLevel=debug --managedManifest=../../tests/fixtures/nw-builder/app/package.json --outDir=../../tests/fixtures/nw-builder/out/linux --app.name=Demo --app.icon=../../tests/fixtures/nw-builder/app/icon.png ../../tests/fixtures/nw-builder/app"
57
56
  },
58
- "devDependencies": {
59
- "base-volta-off-of-nwjs": "^1.0.5",
60
- "nw": "^0.115.0"
61
- },
62
57
  "dependencies": {
63
- "@nwutils/builder": "^0.1.0",
58
+ "@nwutils/builder": "^0.1.2",
64
59
  "@nwutils/getter": "^0.2.5",
65
- "@nwutils/runner": "^0.2.3",
60
+ "@nwutils/packager": "^0.5.0",
61
+ "@nwutils/runner": "^0.2.6",
66
62
  "commander": "^15.0.0",
67
63
  "glob": "^13.0.6"
64
+ },
65
+ "devEngines": {
66
+ "runtime": {
67
+ "name": "node",
68
+ "version": "26.7.0",
69
+ "onFail": "warn"
70
+ },
71
+ "packageManager": {
72
+ "name": "npm",
73
+ "version": "12.0.2",
74
+ "onFail": "warn"
75
+ }
68
76
  }
69
77
  }
package/src/cli.js CHANGED
@@ -8,7 +8,7 @@ import util from "./util.js";
8
8
 
9
9
  program
10
10
  .argument("<string>", "File path(s) to project")
11
- .option("--mode <string>", "get, run or build mode", "build")
11
+ .option("--mode <string>", "get, run, build or package mode", "build")
12
12
  .option("--version <string>", "NW.js version", "latest")
13
13
  .option("--flavor <string>", "NW.js build flavor", "normal")
14
14
  .option(
@@ -38,7 +38,6 @@ program
38
38
  .option(
39
39
  "--app <object>",
40
40
  "Platform specific app metadata. Refer to docs for more info",
41
- /** @type {any} */ ({}),
42
41
  )
43
42
  .option("--cache <boolean>", "Enable/disable caching", true)
44
43
  .option("--ffmpeg <boolean>", "Enable/disable community ffmpeg", false)
@@ -48,18 +47,33 @@ program
48
47
  .option("--zip <string>", "Enable/disable compression", false)
49
48
  .option("--managedManifest <string>", "Managed manifest mode", false)
50
49
  .option("--nodeAddon <boolean>", "Download NW.js Node headers", false)
50
+ .option("--format <string>", "Packaged output format, used in package mode")
51
+ .option(
52
+ "--sign <boolean>",
53
+ "Sign the AppImage with GPG, used in package mode",
54
+ )
55
+ .option(
56
+ "--signKey <string>",
57
+ "GPG key to sign the AppImage with, used in package mode",
58
+ )
59
+ .option(
60
+ "--updateInformation <string>",
61
+ "AppImage zsync update information, or true to derive it from --publish.*",
62
+ )
51
63
  .allowUnknownOption(true)
52
64
  .allowExcessArguments(true);
53
65
 
54
- // Handle unknown --app.* arguments
66
+ // Handle unknown --app.* and --publish.* arguments
55
67
  const unknownArgs = program.parse(process.argv).args;
56
68
  /** @type {Record<string, string>} */
57
69
  const appConfig = {};
70
+ /** @type {Record<string, string>} */
71
+ const publishConfig = {};
58
72
  for (const arg of unknownArgs) {
59
- const match = arg.match(/^--app\.([^.=]+)=(.*)$/);
73
+ const match = arg.match(/^--(app|publish)\.([^.=]+)=(.*)$/);
60
74
  if (match) {
61
- const [, key, value] = match;
62
- appConfig[key] = value;
75
+ const [, group, key, value] = match;
76
+ (group === "app" ? appConfig : publishConfig)[key] = value;
63
77
  }
64
78
  }
65
79
 
@@ -67,7 +81,10 @@ for (const arg of unknownArgs) {
67
81
  const opts = {
68
82
  ...program.opts(),
69
83
  app: appConfig,
70
- srcDir: program.args.find((arg) => !arg.startsWith("--app")),
84
+ publish: Object.keys(publishConfig).length > 0 ? publishConfig : undefined,
85
+ srcDir: program.args.find(
86
+ (arg) => !arg.startsWith("--app") && !arg.startsWith("--publish"),
87
+ ),
71
88
  cli: true,
72
89
  };
73
90
 
package/src/index.d.ts CHANGED
@@ -8,8 +8,8 @@ export type SupportedPlatform = "linux" | "osx" | "win";
8
8
  export interface Options<P extends SupportedPlatform = SupportedPlatform> {
9
9
  /** String of space separated glob patterns which correspond to NW app code */
10
10
  srcDir?: "./" | string;
11
- /** Run or build application */
12
- mode?: "build" | "get" | "run";
11
+ /** Run, build or package application */
12
+ mode?: "build" | "get" | "run" | "package";
13
13
  /** NW runtime version */
14
14
  version?: "latest" | "stable" | string;
15
15
  /** NW runtime flavor */
@@ -42,6 +42,8 @@ export interface Options<P extends SupportedPlatform = SupportedPlatform> {
42
42
  logLevel?: "error" | "warn" | "info" | "debug";
43
43
  /** Managed manifest */
44
44
  managedManifest?: boolean | string | object;
45
+ /** Packaged output format, used in package mode. Defaults to `"AppImage"` on Linux. Only `"AppImage"` is implemented today - the others are reserved. */
46
+ format?: "AppImage" | "deb" | "rpm" | "msix" | "nsis" | "dmg";
45
47
  }
46
48
 
47
49
  /** Platform-specific application options */
@@ -183,6 +185,6 @@ export interface OsxAppOptions {
183
185
  */
184
186
  declare function nwbuild<P extends SupportedPlatform>(
185
187
  options: Options<P>,
186
- ): Promise<ChildProcess | null | undefined>;
188
+ ): Promise<ChildProcess | string | null | undefined>;
187
189
 
188
190
  export default nwbuild;
package/src/index.js CHANGED
@@ -5,13 +5,14 @@ import path from "node:path";
5
5
 
6
6
  import bld from "@nwutils/builder";
7
7
  import get from "@nwutils/getter";
8
+ import pkg from "@nwutils/packager";
8
9
  import run from "@nwutils/runner";
9
10
 
10
11
  import util from "./util.js";
11
12
 
12
13
  /**
13
14
  * @typedef {object} Options Configuration options
14
- * @property {"get" | "run" | "build"} [mode="build"] Choose between get, run or build mode
15
+ * @property {"get" | "run" | "build" | "package"} [mode="build"] Choose between get, run, build or package mode
15
16
  * @property {"latest" | "stable" | string} [version="latest"] Runtime version
16
17
  * @property {"normal" | "sdk"} [flavor="normal"] Runtime flavor
17
18
  * @property {"linux" | "osx" | "win"} [platform] Host platform
@@ -21,17 +22,22 @@ import util from "./util.js";
21
22
  * @property {"./cache" | string} [cacheDir="./cache"] Directory to cache NW binaries
22
23
  * @property {string | string[]} [srcDir="./"] File paths to application code
23
24
  * @property {"./out" | string} [outDir="./out"] Directory to store build artifacts
24
- * @property {object} [app] Refer to Linux/Windows Specific Options under Getting Started in the docs
25
+ * @property {import("./util.js").AppOptions} [app] Refer to Linux/Windows Specific Options under Getting Started in the docs
25
26
  * @property {boolean} [cache=true] If true the existing cache is used. Otherwise it removes and redownloads it.
26
27
  * @property {boolean} [ffmpeg=false] If true the chromium ffmpeg is replaced by community version
27
28
  * @property {boolean} [glob=true] If true file globbing is enabled when parsing srcDir.
28
29
  * @property {"error" | "warn" | "info" | "debug"} [logLevel="info"] Specify level of logging.
29
30
  * @property {boolean} [shaSum = true] If true, shasum is enabled. Otherwise, disabled.
30
31
  * @property {boolean | "zip" | "tar" | "tgz"} [zip=false] If true, "zip", "tar" or "tgz" the outDir directory is compressed.
31
- * @property {boolean | string | object} [managedManifest = false] Managed manifest mode
32
+ * @property {boolean | string | Record<string, unknown>} [managedManifest = false] Managed manifest mode
32
33
  * @property {boolean} [nativeAddon = false] Get Node native addons
33
34
  * @property {boolean} [cli=false] If true the CLI is used to parse options. This option is used internally.
34
35
  * @property {string[]} [argv = []] CLI arguments passed to the NW.js process in run mode
36
+ * @property {"AppImage" | "deb" | "rpm" | "msix" | "nsis" | "dmg"} [format] Packaged output format, used in package mode. Defaults to `"AppImage"` on Linux. Only `"AppImage"` is implemented today - the others are reserved.
37
+ * @property {boolean} [sign=false] If true, embed a GPG signature in the AppImage, used in package mode.
38
+ * @property {string} [signKey] ID of the GPG key to sign the AppImage with, used in package mode. Requires `sign`.
39
+ * @property {import("@nwutils/packager").PublishOptions} [publish] Where releases are published, used in package mode. Makes the AppImage self updatable with `@nwutils/updater`.
40
+ * @property {boolean | string} [updateInformation] AppImage zsync update information, used in package mode. `true` derives it from `publish`.
35
41
  */
36
42
 
37
43
  /**
@@ -39,12 +45,12 @@ import util from "./util.js";
39
45
  * @async
40
46
  * @function
41
47
  * @param {Options} options Options
42
- * @returns {Promise<child_process.ChildProcess | null | undefined>} - Returns NW.js process if run mode, otherwise returns `undefined`.
48
+ * @returns {Promise<child_process.ChildProcess | string | null | undefined>} - Returns the NW.js process in run mode, the path to the packaged artifact in package mode, otherwise returns `undefined`.
43
49
  */
44
50
  async function nwbuild(options) {
45
51
  let built;
46
52
  let releaseInfo;
47
- /** @type {{path: string, json: any}} */
53
+ /** @type {{path: string, json: import("./util.js").PackageManifest | undefined}} */
48
54
  let manifest = {
49
55
  path: "",
50
56
  json: undefined,
@@ -52,7 +58,9 @@ async function nwbuild(options) {
52
58
 
53
59
  try {
54
60
  /* Parse options */
55
- options = await util.parse(options, manifest);
61
+ options = /** @type {Options} */ (
62
+ await util.parse(options, manifest.json ?? {})
63
+ );
56
64
  util.log("debug", "info", "Parse initial options");
57
65
 
58
66
  util.log("debug", "info", "Get node manifest...");
@@ -69,8 +77,9 @@ async function nwbuild(options) {
69
77
  /** @type {"debug" | "error" | "info" | "warn"} */ (options.logLevel),
70
78
  "Parse final options using node manifest",
71
79
  );
72
- /** @type {Required<Options>} */
73
- const resolved = await util.parse(options, manifest.json);
80
+ const resolved = /** @type {Required<Options>} */ (
81
+ await util.parse(options, manifest.json ?? {})
82
+ );
74
83
  util.log(
75
84
  "debug",
76
85
  resolved.logLevel,
@@ -113,7 +122,9 @@ async function nwbuild(options) {
113
122
  );
114
123
 
115
124
  /* Remove leading "v" from version string */
116
- resolved.version = releaseInfo.version.slice(1);
125
+ resolved.version = /** @type {import("./util.js").ReleaseInfo} */ (
126
+ releaseInfo
127
+ ).version.slice(1);
117
128
 
118
129
  util.log(
119
130
  "info",
@@ -158,7 +169,7 @@ async function nwbuild(options) {
158
169
  argv: resolved.argv,
159
170
  });
160
171
  return nwProcess;
161
- } else if (resolved.mode === "build") {
172
+ } else if (resolved.mode === "build" || resolved.mode === "package") {
162
173
  util.log(
163
174
  "info",
164
175
  resolved.logLevel,
@@ -173,7 +184,9 @@ async function nwbuild(options) {
173
184
  srcDir: /** @type {string} */ (resolved.srcDir),
174
185
  cacheDir: resolved.cacheDir,
175
186
  outDir: resolved.outDir,
176
- app: /** @type {any} */ (resolved.app),
187
+ app: /** @type {import("@nwutils/builder").LinuxRc | import("@nwutils/builder").WinRc | import("@nwutils/builder").OsxRc} */ (
188
+ resolved.app
189
+ ),
177
190
  glob: resolved.glob,
178
191
  managedManifest: resolved.managedManifest,
179
192
  zip: resolved.zip,
@@ -184,6 +197,34 @@ async function nwbuild(options) {
184
197
  resolved.logLevel,
185
198
  `Appliction is available at ${path.resolve(resolved.outDir)}`,
186
199
  );
200
+
201
+ if (resolved.mode === "package") {
202
+ util.log(
203
+ "info",
204
+ resolved.logLevel,
205
+ `Packaging NW.js application as ${resolved.format}...`,
206
+ );
207
+ const packagePath = await pkg({
208
+ format: resolved.format,
209
+ appDir: resolved.outDir,
210
+ appName: resolved.app.name,
211
+ icon: resolved.app.icon,
212
+ arch: resolved.arch,
213
+ cacheDir: resolved.cacheDir,
214
+ cache: resolved.cache,
215
+ sign: resolved.sign,
216
+ signKey: resolved.signKey,
217
+ version: manifest.json?.version,
218
+ publish: resolved.publish,
219
+ updateInformation: resolved.updateInformation,
220
+ });
221
+ util.log(
222
+ "info",
223
+ resolved.logLevel,
224
+ `Package is available at ${packagePath}`,
225
+ );
226
+ return packagePath;
227
+ }
187
228
  }
188
229
  } catch (error) {
189
230
  console.error(error);
package/src/util.js CHANGED
@@ -7,6 +7,122 @@ import url from "node:url";
7
7
 
8
8
  import * as GlobModule from "glob";
9
9
 
10
+ /**
11
+ * Linux/Windows/OSX specific application configuration. Every property maps
12
+ * to a `LinuxRc`/`WinRc`/`OsxRc` field from `@nwutils/builder`, kept optional
13
+ * here since which ones are required depends on `options.platform` - that is
14
+ * checked at runtime by `validate()`.
15
+ * @typedef {object} AppOptions
16
+ * @property {string} [name] Name of the application
17
+ * @property {string} [icon] Path to icon file
18
+ * @property {string} [genericName] Generic name of the application
19
+ * @property {boolean} [noDisplay] If true the application is not displayed
20
+ * @property {string} [comment] Tooltip for the entry
21
+ * @property {string | boolean} [hidden] Whether the entry is hidden
22
+ * @property {string[]} [onlyShowIn] Desktop environments that should display this entry
23
+ * @property {string[]} [notShowIn] Desktop environments that should not display this entry
24
+ * @property {boolean} [dBusActivatable] If true D-Bus activation is supported
25
+ * @property {string} [tryExec] Executable used to determine if the program is installed
26
+ * @property {string} [exec] Program to execute, possibly with arguments
27
+ * @property {string} [path] Working directory to run the program in
28
+ * @property {boolean} [terminal] Whether the program runs in a terminal window
29
+ * @property {string[]} [actions] Identifiers for application actions
30
+ * @property {string[]} [mimeType] MIME type(s) supported by this application
31
+ * @property {string[]} [categories] Menu categories the entry should be shown in
32
+ * @property {string[]} [implements] Interfaces this application implements
33
+ * @property {string[]} [keywords] Additional strings used to describe this entry
34
+ * @property {boolean} [startupNotify] Whether the application supports startup notification
35
+ * @property {string} [startupWMClass] WM class or WM name hint the application maps
36
+ * @property {boolean} [prefersNonDefaultGPU] If true prefers running on a discrete GPU
37
+ * @property {string} [singleMainWindow] If true the application has a single main window
38
+ * @property {string} [version] Version of the application
39
+ * @property {string} [comments] Additional diagnostic information
40
+ * @property {string} [company] Company that produced the file
41
+ * @property {string} [fileDescription] File description presented to users
42
+ * @property {string} [fileVersion] Version number of the file
43
+ * @property {string} [internalName] Internal name of the file
44
+ * @property {string} [legalCopyright] Copyright notices that apply to the file
45
+ * @property {string} [legalTrademark] Trademarks that apply to the file
46
+ * @property {string} [originalFilename] Original name of the file
47
+ * @property {string} [privateBuild] Information about a private build
48
+ * @property {string} [productName] Name of the product the file is distributed with
49
+ * @property {string} [productVersion] Version of the product the file is distributed with
50
+ * @property {string} [specialBuild] How this build differs from the standard version
51
+ * @property {number} [languageCode] Language identifier (LCID) of the file
52
+ * @property {string} [LSApplicationCategoryType] App Store category
53
+ * @property {string} [CFBundleIdentifier] Unique bundle identifier, usually reverse DNS
54
+ * @property {string} [CFBundleName] User-visible short name for the bundle
55
+ * @property {string} [CFBundleDisplayName] User-visible name for the bundle
56
+ * @property {string} [CFBundleSpokenName] Replacement app name for text-to-speech
57
+ * @property {string} [CFBundleShortVersionString] Release or version number of the bundle
58
+ * @property {string} [CFBundleVersion] Build version that identifies a bundle iteration
59
+ * @property {string} [NSHumanReadableCopyright] Human-readable copyright notice
60
+ * @property {string} [NSLocalNetworkUsageDescription] Why the app needs local network access
61
+ * @property {boolean} [LSFileQuarantineEnabled] Whether files this app creates are quarantined
62
+ */
63
+
64
+ /**
65
+ * Minimal shape of the `package.json` fields read while resolving options.
66
+ * @typedef {object} PackageManifest
67
+ * @property {string} [name] Package name
68
+ * @property {string} [version] Package version
69
+ * @property {string} [author] Package author
70
+ * @property {string} [description] Package description
71
+ * @property {{icon?: string}} [window] NW.js manifest `window` configuration
72
+ * @property {object} [nwbuild] `nwbuild` options embedded in the manifest
73
+ */
74
+
75
+ /**
76
+ * Version specific NW.js release metadata, as found in the `versions` array
77
+ * of the manifest at `manifestUrl`.
78
+ * @typedef {object} ReleaseInfo
79
+ * @property {string} version NW.js version, eg. `"v0.115.0"`
80
+ * @property {string[]} flavors Flavors this release is published for
81
+ * @property {string[]} files Supported `<platform>-<arch>` combinations
82
+ */
83
+
84
+ /**
85
+ * The versions manifest NW.js releases are published to.
86
+ * @typedef {object} VersionsManifest
87
+ * @property {string} latest Latest published version
88
+ * @property {string} stable Latest stable version
89
+ * @property {string} lts Latest LTS version
90
+ * @property {ReleaseInfo[]} versions Every published release
91
+ */
92
+
93
+ /**
94
+ * `nwbuild` options, partially or fully resolved. Every field is optional
95
+ * and loosely typed as supplied by the caller or CLI (eg. `cache` may arrive
96
+ * as the string `"true"`) - `parse()` normalizes them in place.
97
+ * @typedef {object} Options
98
+ * @property {"get" | "run" | "build" | "package"} [mode] Choose between get, run, build or package mode
99
+ * @property {string} [version] Runtime version
100
+ * @property {"normal" | "sdk"} [flavor] Runtime flavor
101
+ * @property {"linux" | "osx" | "win"} [platform] Host platform
102
+ * @property {"ia32" | "x64" | "arm64"} [arch] Host architecture
103
+ * @property {string} [downloadUrl] Download server
104
+ * @property {string} [manifestUrl] Versions manifest URI
105
+ * @property {string} [cacheDir] Directory to cache NW binaries
106
+ * @property {boolean | string} [cache] If true the existing cache is used
107
+ * @property {boolean | string} [ffmpeg] If true the community ffmpeg is used
108
+ * @property {"error" | "warn" | "info" | "debug"} [logLevel] Specify level of logging
109
+ * @property {boolean | string} [shaSum] If true, shasum is enabled
110
+ * @property {boolean | string} [nativeAddon] Get Node native addons
111
+ * @property {"AppImage" | "deb" | "rpm" | "msix" | "nsis" | "dmg"} [format] Packaged output format, used in package mode
112
+ * @property {boolean | string} [sign] If true, sign the AppImage, used in package mode
113
+ * @property {string} [signKey] GPG key to sign the AppImage with, used in package mode
114
+ * @property {import("@nwutils/packager").PublishOptions} [publish] Where releases are published, used in package mode
115
+ * @property {boolean | string} [updateInformation] AppImage zsync update information, used in package mode
116
+ * @property {string[]} [argv] CLI arguments passed to the NW.js process in run mode
117
+ * @property {boolean | string} [glob] If true file globbing is enabled when parsing srcDir
118
+ * @property {string | string[]} [srcDir] File paths to application code
119
+ * @property {string} [outDir] Directory to store build artifacts
120
+ * @property {boolean | string} [zip] If truthy, the outDir directory is compressed
121
+ * @property {boolean | string | Record<string, unknown>} [managedManifest] Managed manifest mode
122
+ * @property {AppOptions} [app] Platform specific app metadata
123
+ * @property {boolean} [cli] If true the CLI is used to parse options
124
+ */
125
+
10
126
  /**
11
127
  * Get manifest (array of NW release metadata) from URL.
12
128
  * @param {string} manifestUrl Versions manifest URI, https or file path
@@ -49,7 +165,7 @@ function getManifest(manifestUrl) {
49
165
  * @param {string} arch NW architecture
50
166
  * @param {string} cacheDir Directory to store NW binaries
51
167
  * @param {string} manifestUrl Versions manifest URI, https or file path
52
- * @returns {Promise<any>} Version specific release info
168
+ * @returns {Promise<ReleaseInfo | undefined>} Version specific release info
53
169
  */
54
170
  async function getReleaseInfo(version, platform, arch, cacheDir, manifestUrl) {
55
171
  let releaseData = undefined;
@@ -66,7 +182,7 @@ async function getReleaseInfo(version, platform, arch, cacheDir, manifestUrl) {
66
182
  await fs.promises.writeFile(manifestPath, data);
67
183
  }
68
184
 
69
- /** @type {any} */
185
+ /** @type {VersionsManifest} */
70
186
  const manifest = JSON.parse(
71
187
  await fs.promises.readFile(manifestPath, "utf8"),
72
188
  );
@@ -76,7 +192,7 @@ async function getReleaseInfo(version, platform, arch, cacheDir, manifestUrl) {
76
192
  }
77
193
 
78
194
  releaseData = manifest.versions.find(
79
- (/** @type {any} */ release) => release.version === `v${version}`,
195
+ (release) => release.version === `v${version}`,
80
196
  );
81
197
  } catch {
82
198
  console.error(
@@ -86,12 +202,14 @@ async function getReleaseInfo(version, platform, arch, cacheDir, manifestUrl) {
86
202
  return releaseData;
87
203
  }
88
204
 
205
+ /** @type {Record<"darwin" | "linux" | "win32", "linux" | "osx" | "win">} */
89
206
  const PLATFORM_KV = {
90
207
  darwin: "osx",
91
208
  linux: "linux",
92
209
  win32: "win",
93
210
  };
94
211
 
212
+ /** @type {Record<"x64" | "ia32" | "arm64", "x64" | "ia32" | "arm64">} */
95
213
  const ARCH_KV = {
96
214
  x64: "x64",
97
215
  ia32: "ia32",
@@ -143,10 +261,10 @@ async function globFiles({ srcDir, glob }) {
143
261
  * @param {object} options - node manifest options
144
262
  * @param {string | string []} options.srcDir - src dir
145
263
  * @param {boolean} options.glob - glob flag
146
- * @returns {Promise.<{path: string, json: object}>} - Node manifest
264
+ * @returns {Promise<{path: string, json: PackageManifest | undefined}>} - Node manifest
147
265
  */
148
266
  async function getNodeManifest({ srcDir, glob }) {
149
- /** @type {{path: string, json: any}} */
267
+ /** @type {{path: string, json: PackageManifest | undefined}} */
150
268
  let manifest = {
151
269
  path: "",
152
270
  json: undefined,
@@ -184,24 +302,31 @@ async function getNodeManifest({ srcDir, glob }) {
184
302
  /**
185
303
  * Function to convert `'true'` and `'false'` into `true` and `false`.
186
304
  * `commander` does not do the conversion automatically.
187
- * @param {string} option - a boolean type option
188
- * @returns {true | false | string} `true`, `false` or file path
305
+ * @template {string | boolean | Record<string, unknown> | undefined} T
306
+ * @param {T} option - a boolean type option
307
+ * @returns {T extends "true" ? true : T extends "false" ? false : T} `true`, `false` or file path
189
308
  */
190
309
  function str2Bool(option) {
191
310
  if (option === "true") {
192
- return true;
311
+ return /** @type {T extends "true" ? true : T extends "false" ? false : T} */ (
312
+ true
313
+ );
193
314
  } else if (option === "false") {
194
- return false;
315
+ return /** @type {T extends "true" ? true : T extends "false" ? false : T} */ (
316
+ false
317
+ );
195
318
  } else {
196
- return option;
319
+ return /** @type {T extends "true" ? true : T extends "false" ? false : T} */ (
320
+ option
321
+ );
197
322
  }
198
323
  }
199
324
 
200
325
  /**
201
326
  * Parse options.
202
- * @param {any} options Options
203
- * @param {any} pkg Package.json as JSON
204
- * @returns {Promise<any>} Options
327
+ * @param {Options | undefined} options Options
328
+ * @param {PackageManifest} pkg Package.json as JSON
329
+ * @returns {Promise<Options>} Options
205
330
  */
206
331
  export const parse = async (options, pkg) => {
207
332
  options = options ?? {};
@@ -228,6 +353,13 @@ export const parse = async (options, pkg) => {
228
353
  return { ...options };
229
354
  }
230
355
 
356
+ if (options.mode === "package") {
357
+ options.format =
358
+ options.format ?? (options.platform === "linux" ? "AppImage" : undefined);
359
+ options.sign = str2Bool(options.sign ?? false);
360
+ options.updateInformation = str2Bool(options.updateInformation);
361
+ }
362
+
231
363
  options.argv = options.argv ?? [];
232
364
  options.glob = str2Bool(options.glob) ?? true;
233
365
  options.srcDir = options.srcDir ?? (options.glob ? "./*" : ".");
@@ -327,8 +459,8 @@ export const parse = async (options, pkg) => {
327
459
 
328
460
  /**
329
461
  * Validate options.
330
- * @param {any} options Options
331
- * @param {any} releaseInfo Version specific NW release info
462
+ * @param {Options} options Options
463
+ * @param {ReleaseInfo | undefined} releaseInfo Version specific NW release info
332
464
  * @returns {Promise<undefined>} Return undefined if options are valid
333
465
  * @throws {Error} Throw error if options are invalid
334
466
  */
@@ -336,10 +468,11 @@ export const validate = async (options, releaseInfo) => {
336
468
  if (
337
469
  options.mode !== "get" &&
338
470
  options.mode !== "run" &&
339
- options.mode !== "build"
471
+ options.mode !== "build" &&
472
+ options.mode !== "package"
340
473
  ) {
341
474
  throw new Error(
342
- `Unknown mode ${options.mode}. Expected "get", "run" or "build".`,
475
+ `Unknown mode ${options.mode}. Expected "get", "run", "build" or "package".`,
343
476
  );
344
477
  }
345
478
  if (typeof releaseInfo === "undefined") {
@@ -347,6 +480,8 @@ export const validate = async (options, releaseInfo) => {
347
480
  "Either the specific version info does not exist or the version manifest itself does not exist. In case of the latter, please check your internet connection and try again later.",
348
481
  );
349
482
  }
483
+ /* By the time validate() runs, parse() has already defaulted options.flavor. */
484
+ options.flavor = options.flavor ?? "normal";
350
485
  if (!releaseInfo.flavors.includes(options.flavor)) {
351
486
  throw new Error(
352
487
  `${options.flavor} flavor is not supported by this download server.`,
@@ -484,6 +619,30 @@ export const validate = async (options, releaseInfo) => {
484
619
  );
485
620
  }
486
621
 
622
+ if (options.mode === "package") {
623
+ if (
624
+ options.format !== "AppImage" &&
625
+ options.format !== "deb" &&
626
+ options.format !== "rpm" &&
627
+ options.format !== "msix" &&
628
+ options.format !== "nsis" &&
629
+ options.format !== "dmg"
630
+ ) {
631
+ throw new Error(
632
+ 'Expected options.format to be one of "AppImage", "deb", "rpm", "msix", "nsis" or "dmg". Got ' +
633
+ JSON.stringify(options.format),
634
+ );
635
+ }
636
+ if (options.format === "AppImage" && options.platform !== "linux") {
637
+ throw new Error(
638
+ `options.format "AppImage" requires options.platform to be "linux". Got ${JSON.stringify(options.platform)}.`,
639
+ );
640
+ }
641
+ }
642
+
643
+ /* By the time validate() runs, parse() has already defaulted options.app to {}. */
644
+ options.app = options.app ?? {};
645
+
487
646
  if (options.platform === "linux") {
488
647
  if (options.app.name && typeof options.app.name !== "string") {
489
648
  throw new Error(
@@ -852,7 +1011,7 @@ export const validate = async (options, releaseInfo) => {
852
1011
  * @async
853
1012
  * @function
854
1013
  * @param {"chromedriver"} type - NW specific file or directory
855
- * @param {any} options - nwbuild options
1014
+ * @param {{cacheDir: string, flavor: string, version: string, platform: string, arch: string}} options - nwbuild options
856
1015
  * @returns {Promise<string>} - Path to chromedriver
857
1016
  * @throws {Error}
858
1017
  */
@@ -1,19 +0,0 @@
1
- import path from "node:path";
2
- import url from "node:url";
3
-
4
- const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
5
-
6
- const baseVoltaPath = path.resolve(
7
- path.join(
8
- __dirname,
9
- "..",
10
- "node_modules",
11
- "base-volta-off-of-nwjs",
12
- "index.js",
13
- ),
14
- );
15
-
16
- /* Execute the script in development mode only since it is used during testing */
17
- import(baseVoltaPath)
18
- .then(() => console.log("Node version is updated"))
19
- .catch((error) => console.log(error));