nw-builder 4.18.1 → 4.19.1

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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2021-2024 NW.js Utilities
3
+ Copyright (c) 2021-2024 NW.js Utilities and contributors
4
4
  Copyright (c) 2014-2021 Steffen Müller
5
5
 
6
6
  Permission is hereby granted, free of charge, to any person obtaining a copy of
package/README.md CHANGED
@@ -234,7 +234,7 @@ This object defines additional properties used for building for a specific platf
234
234
  | `productName` | `string` | `name` in NW.js manifest | Name of the product with which the file is distributed. This string is required. |
235
235
  | `productVersion` | `string` | Value of `version` in NW.js manifest | Version of the product with which the file is distributed—for example, 3.10 or 5.00.RC2. |
236
236
  | `specialBuild` | `string` | `undefined` | Text that specifies how this version of the file differs from the standard version—for example, Private build for TESTER1 solving mouse problems on M250 and M250E computers. |
237
- | `languageCode` | `number` | `1033` | Language of the file, defined by Microsoft, see: https://learn.microsoft.com/en-us/openspecs/office_standards/ms-oe376/6c085406-a698-4e12-9d4d-c3b0ee3dbc4a |
237
+ | `languageCode` | `number` | `1033` | Language of the file, defined by Microsoft, see [MS-OE376](https://learn.microsoft.com/en-us/openspecs/office_standards/ms-oe376)6c085406-a698-4e12-9d4d-c3b0ee3dbc4a |
238
238
 
239
239
  #### Linux-specific options (`LinuxRc`)
240
240
 
@@ -265,19 +265,20 @@ This object defines additional properties used for building for a specific platf
265
265
 
266
266
  #### MacOS-specific options (`OsxRc`)
267
267
 
268
- | Name | Type | Description |
269
- | ------------------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------- |
270
- | name | `string` | The name of the application |
271
- | icon | `string` | The path to the icon file. It should be a .icns file. (**WARNING**: Please define the icon in the NW.js manifest instead) |
272
- | LSApplicationCategoryType | `string` | The category that best describes your app for the App Store. |
273
- | CFBundleIdentifier | `string` | A unique identifier for a bundle usually in reverse DNS format. |
274
- | CFBundleName | `string` | A user-visible short name for the bundle. |
275
- | CFBundleDisplayName | `string` | The user-visible name for the bundle. |
276
- | CFBundleSpokenName | `string` | A replacement for the app name in text-to-speech operations. |
277
- | CFBundleVersion | `string` | The version of the build that identifies an iteration of the bundle. |
278
- | CFBundleShortVersionString | `string` | The release or version number of the bundle. |
279
- | NSHumanReadableCopyright | `string` | A human-readable copyright notice for the bundle. |
280
- | NSLocalNetworkUsageDescription | `string` | A human-readable description of why the application needs access to the local network. |
268
+ | Name | Type | Description |
269
+ | ------------------------------ | --------- | ------------------------------------------------------------------------------------------------------------------------- |
270
+ | name | `string` | The name of the application |
271
+ | icon | `string` | The path to the icon file. It should be a .icns file. (**WARNING**: Please define the icon in the NW.js manifest instead) |
272
+ | LSApplicationCategoryType | `string` | The category that best describes your app for the App Store. |
273
+ | CFBundleIdentifier | `string` | A unique identifier for a bundle usually in reverse DNS format. |
274
+ | CFBundleName | `string` | A user-visible short name for the bundle. |
275
+ | CFBundleDisplayName | `string` | The user-visible name for the bundle. |
276
+ | CFBundleSpokenName | `string` | A replacement for the app name in text-to-speech operations. |
277
+ | CFBundleVersion | `string` | The version of the build that identifies an iteration of the bundle. |
278
+ | CFBundleShortVersionString | `string` | The release or version number of the bundle. |
279
+ | NSHumanReadableCopyright | `string` | A human-readable copyright notice for the bundle. |
280
+ | NSLocalNetworkUsageDescription | `string` | A human-readable description of why the application needs access to the local network. |
281
+ | LSFileQuarantineEnabled | `boolean` | A Boolean value indicating whether the files this app creates are quarantined by default. |
281
282
 
282
283
  ## Guides
283
284
 
@@ -328,55 +329,10 @@ nwbuild({
328
329
 
329
330
  ### External contributor
330
331
 
331
- - We use [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) style of commit messages.
332
- - On opening a new PR, the comments will guide you on how to construct the new PR.
333
- - Pull requests are squashed and merged onto the `main` branch.
334
- - PR title is used as commit's first line, PR description is used as commit body.
335
- - Only commit messages starting with `fix`, `feat` and `chore` are recognised by the Release Please bot.
336
- - Lint your code before commiting your change.
337
- - Add tests whenever possible.
332
+ - Use Node.js standard libraries whenever possible.
333
+ - Prefer to use syncronous APIs over modern APIs which have been introduced in later versions.
338
334
 
339
- ### Maintainer guidelines
335
+ ### Maintainer
340
336
 
341
- - Approve pull requests before merging.
342
- - Enforce conventional commits before merging pull requests.
343
- - A commit's first line should be formatted as `<type>[optional scope]: <description>`.
344
- - A commit's body should have a description of changes in bullet points followed by any links it references or issues it fixes or closes.
345
- - Google's Release Please Action is used to update the changelog, bump the package version and generate GitHub releases.
346
- - NPM Publish Action publishes to `npm` if there is a version bump.
347
- - Every time `nw` is upgraded, the `volta` package should also be updated and `nw-builder` should create a minor release (since the underlying Node version is changed).
348
-
349
- ## Roadmap
350
-
351
- ### Bugs
352
-
353
- - Managed Manifest is broken. If glob is disabled and srcDir has no package.json, build fails.
354
- - Add back error, info, warn and debug logs
355
-
356
- ### Features
357
-
358
- - feat(get): support canary releases
359
- - feat(pkg): add `AppImage` installer
360
- - feat(pkg): add `NSIS` installer
361
- - feat(pkg): add `DMG` installer
362
- - feat(get): add Linux ARM unofficial support
363
- - feat(bld): add source code protection
364
- - feat(pkg): add code signing
365
-
366
- ### Chores
367
-
368
- - chore(docs): don't store JSDoc definitions in `typedef`s - get's hard to understand during development.
369
- - chore: annotate file paths as `fs.PathLike` instead of `string`.
370
- - chore(bld): factor out core build step
371
- - chore(bld): factor out linux config
372
- - chore(bld): factor out windows config
373
- - chore(bld): factor out native addon
374
- - chore(bld): factor out compressing
375
- - chore(bld): factor out managed manifest
376
- - chore(bld): move `.desktop` entry file logic to `create-desktop-shortcuts` package
377
-
378
- ## FAQ
379
-
380
- ## License
381
-
382
- MIT License.
337
+ - npm Trusted Publishing with OIDC is used for releases
338
+ - A package is released when a maintainer creates a release note for a specific version
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nw-builder",
3
- "version": "4.18.1",
3
+ "version": "4.19.1",
4
4
  "description": "Build NW.js desktop applications for MacOS, Windows and Linux.",
5
5
  "keywords": [
6
6
  "NW.js",
@@ -42,46 +42,27 @@
42
42
  },
43
43
  "scripts": {
44
44
  "postinstall": "node ./src/postinstall.js",
45
- "format": "prettier --check .",
46
- "format:fix": "prettier --write .",
47
- "lint": "eslint ./src ./tests",
48
- "lint:fix": "eslint --fix ./src ./tests",
49
- "test": "vitest run --coverage",
50
- "test:cov": "vitest --coverage.enabled true",
51
- "demo:bld:linux": "node ./tests/fixtures/demo.linux.js",
52
- "demo:bld:osx": "node ./tests/fixtures/demo.osx.js",
53
- "demo:bld:win": "node ./tests/fixtures/demo.win.js",
54
- "demo:exe:linux": "./tests/fixtures/out/linux/Demo",
55
- "demo:exe:osx": "./tests/fixtures/out/osx/Demo.app/Contents/MacOS/Demo",
56
- "demo:exe:win": "./tests/fixtures/out/win/Demo.exe",
57
- "demo:cli": "nwbuild --mode=build --flavor=sdk --glob=false --cacheDir=./node_modules/nw --logLevel=debug --managedManifest=./tests/fixtures/app/package.json --outDir=./tests/fixtures/out/linux --app.name=Demo --app.icon=./tests/fixtures/app/icon.png ./tests/fixtures/app"
45
+ "lint": "eslint ./src ../../tests/specs/nw-builder",
46
+ "lint:fix": "eslint --fix ./src ../../tests/specs/nw-builder",
47
+ "type": "tsc",
48
+ "test": "node --test \"../../tests/specs/nw-builder/*.test.js\"",
49
+ "demo:bld:linux": "node ../../tests/fixtures/nw-builder/demo.linux.js",
50
+ "demo:bld:osx": "node ../../tests/fixtures/nw-builder/demo.osx.js",
51
+ "demo:bld:win": "node ../../tests/fixtures/nw-builder/demo.win.js",
52
+ "demo:exe:linux": "../../tests/fixtures/nw-builder/out/linux/Demo",
53
+ "demo:exe:osx": "../../tests/fixtures/nw-builder/out/osx/Demo.app/Contents/MacOS/Demo",
54
+ "demo:exe:win": "../../tests/fixtures/nw-builder/out/win/Demo.exe",
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"
58
56
  },
59
57
  "devDependencies": {
60
- "@eslint/js": "^10.0.1",
61
- "@vitest/coverage-v8": "^4.1.10",
62
58
  "base-volta-off-of-nwjs": "^1.0.5",
63
- "eslint": "^10.8.1",
64
- "eslint-plugin-jsdoc": "^64.2.0",
65
- "eslint-plugin-simple-import-sort": "^14.0.0",
66
- "globals": "^17.11.0",
67
- "nw": "^0.114.1",
68
- "prettier": "^3.9.6",
69
- "selenium-webdriver": "^4.47.0",
70
- "vitest": "^4.0.14"
59
+ "nw": "^0.115.0"
71
60
  },
72
61
  "dependencies": {
62
+ "@nwutils/builder": "^0.1.0",
73
63
  "@nwutils/getter": "^0.2.5",
74
64
  "@nwutils/runner": "^0.2.3",
75
- "archiver": "^8.0.0",
76
65
  "commander": "^15.0.0",
77
- "glob": "^13.0.6",
78
- "plist": "^5.0.0",
79
- "resedit": "^3.0.2",
80
- "semver": "^7.8.5",
81
- "tar": "^7.5.19"
82
- },
83
- "volta": {
84
- "node": "26.7.0",
85
- "npm": "11.13.0"
66
+ "glob": "^13.0.6"
86
67
  }
87
68
  }
package/src/cli.js CHANGED
@@ -14,12 +14,14 @@ program
14
14
  .option(
15
15
  "--platform <string>",
16
16
  "NW.js supported platform",
17
- util.PLATFORM_KV[process.platform],
17
+ util.PLATFORM_KV[
18
+ /** @type {"darwin" | "linux" | "win32"} */ (process.platform)
19
+ ],
18
20
  )
19
21
  .option(
20
22
  "--arch <string>",
21
23
  "NW.js supported architecture",
22
- util.ARCH_KV[process.arch],
24
+ util.ARCH_KV[/** @type {"x64" | "ia32" | "arm64"} */ (process.arch)],
23
25
  )
24
26
  .option(
25
27
  "--downloadUrl <string>",
@@ -36,7 +38,7 @@ program
36
38
  .option(
37
39
  "--app <object>",
38
40
  "Platform specific app metadata. Refer to docs for more info",
39
- {},
41
+ /** @type {any} */ ({}),
40
42
  )
41
43
  .option("--cache <boolean>", "Enable/disable caching", true)
42
44
  .option("--ffmpeg <boolean>", "Enable/disable community ffmpeg", false)
@@ -51,6 +53,7 @@ program
51
53
 
52
54
  // Handle unknown --app.* arguments
53
55
  const unknownArgs = program.parse(process.argv).args;
56
+ /** @type {Record<string, string>} */
54
57
  const appConfig = {};
55
58
  for (const arg of unknownArgs) {
56
59
  const match = arg.match(/^--app\.([^.=]+)=(.*)$/);
package/src/index.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import type { ChildProcess } from "node:child_process";
2
+
1
3
  /** NW supported platform */
2
4
  export type SupportedPlatform = "linux" | "osx" | "win";
3
5
 
@@ -172,6 +174,8 @@ export interface OsxAppOptions {
172
174
  NSHumanReadableCopyright?: string;
173
175
  /** A human-readable description of why the application needs access to the local network. */
174
176
  NSLocalNetworkUsageDescription?: string;
177
+ /** A Boolean value indicating whether the files this app creates are quarantined by default. */
178
+ LSFileQuarantineEnabled?: boolean;
175
179
  }
176
180
 
177
181
  /**
@@ -179,6 +183,6 @@ export interface OsxAppOptions {
179
183
  */
180
184
  declare function nwbuild<P extends SupportedPlatform>(
181
185
  options: Options<P>,
182
- ): Promise<child_process.ChildProcess | null | undefined>;
186
+ ): Promise<ChildProcess | null | undefined>;
183
187
 
184
188
  export default nwbuild;
package/src/index.js CHANGED
@@ -3,10 +3,10 @@ import console from "node:console";
3
3
  import fs from "node:fs";
4
4
  import path from "node:path";
5
5
 
6
+ import bld from "@nwutils/builder";
6
7
  import get from "@nwutils/getter";
7
8
  import run from "@nwutils/runner";
8
9
 
9
- import bld from "./bld.js";
10
10
  import util from "./util.js";
11
11
 
12
12
  /**
@@ -14,14 +14,14 @@ import util from "./util.js";
14
14
  * @property {"get" | "run" | "build"} [mode="build"] Choose between get, run or build mode
15
15
  * @property {"latest" | "stable" | string} [version="latest"] Runtime version
16
16
  * @property {"normal" | "sdk"} [flavor="normal"] Runtime flavor
17
- * @property {"linux" | "osx" | "win"} platform Host platform
18
- * @property {"ia32" | "x64" | "arm64"} arch Host architecture
17
+ * @property {"linux" | "osx" | "win"} [platform] Host platform
18
+ * @property {"ia32" | "x64" | "arm64"} [arch] Host architecture
19
19
  * @property {"https://dl.nwjs.io" | string} [downloadUrl="https://dl.nwjs.io"] Download server
20
20
  * @property {"https://nwjs.io/versions.json" | string} [manifestUrl="https://nwjs.io/versions.json"] Versions manifest URI, https or file path
21
21
  * @property {"./cache" | string} [cacheDir="./cache"] Directory to cache NW binaries
22
- * @property {"./" | string} [srcDir="./"] File paths to application code
22
+ * @property {string | string[]} [srcDir="./"] File paths to application code
23
23
  * @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
24
+ * @property {object} [app] Refer to Linux/Windows Specific Options under Getting Started in the docs
25
25
  * @property {boolean} [cache=true] If true the existing cache is used. Otherwise it removes and redownloads it.
26
26
  * @property {boolean} [ffmpeg=false] If true the chromium ffmpeg is replaced by community version
27
27
  * @property {boolean} [glob=true] If true file globbing is enabled when parsing srcDir.
@@ -29,8 +29,9 @@ import util from "./util.js";
29
29
  * @property {boolean} [shaSum = true] If true, shasum is enabled. Otherwise, disabled.
30
30
  * @property {boolean | "zip" | "tar" | "tgz"} [zip=false] If true, "zip", "tar" or "tgz" the outDir directory is compressed.
31
31
  * @property {boolean | string | object} [managedManifest = false] Managed manifest mode
32
- * @property {boolean} [nodeAddon = false] Get Node native addons
32
+ * @property {boolean} [nativeAddon = false] Get Node native addons
33
33
  * @property {boolean} [cli=false] If true the CLI is used to parse options. This option is used internally.
34
+ * @property {string[]} [argv = []] CLI arguments passed to the NW.js process in run mode
34
35
  */
35
36
 
36
37
  /**
@@ -43,6 +44,7 @@ import util from "./util.js";
43
44
  async function nwbuild(options) {
44
45
  let built;
45
46
  let releaseInfo;
47
+ /** @type {{path: string, json: any}} */
46
48
  let manifest = {
47
49
  path: "",
48
50
  json: undefined,
@@ -55,8 +57,8 @@ async function nwbuild(options) {
55
57
 
56
58
  util.log("debug", "info", "Get node manifest...");
57
59
  manifest = await util.getNodeManifest({
58
- srcDir: options.srcDir,
59
- glob: options.glob,
60
+ srcDir: /** @type {string | string[]} */ (options.srcDir),
61
+ glob: /** @type {boolean} */ (options.glob),
60
62
  });
61
63
  if (typeof manifest.json?.nwbuild === "object") {
62
64
  options = { ...options, ...manifest.json.nwbuild };
@@ -64,118 +66,123 @@ async function nwbuild(options) {
64
66
 
65
67
  util.log(
66
68
  "info",
67
- options.logLevel,
69
+ /** @type {"debug" | "error" | "info" | "warn"} */ (options.logLevel),
68
70
  "Parse final options using node manifest",
69
71
  );
70
- options = await util.parse(options, manifest.json);
72
+ /** @type {Required<Options>} */
73
+ const resolved = await util.parse(options, manifest.json);
71
74
  util.log(
72
75
  "debug",
73
- options.logLevel,
74
- "Manifest: ",
75
- `${manifest.path}\n${manifest.json}\n`,
76
+ resolved.logLevel,
77
+ `Manifest: ${manifest.path}\n${manifest.json}\n`,
76
78
  );
77
79
 
78
- built = fs.existsSync(options.cacheDir);
80
+ built = fs.existsSync(resolved.cacheDir);
79
81
  if (built === false) {
80
- await fs.promises.mkdir(options.cacheDir, { recursive: true });
82
+ await fs.promises.mkdir(resolved.cacheDir, { recursive: true });
81
83
  }
82
84
 
83
- if (options.mode === "build") {
84
- built = fs.existsSync(options.outDir);
85
+ if (resolved.mode === "build") {
86
+ built = fs.existsSync(resolved.outDir);
85
87
  if (built === false) {
86
- await fs.promises.mkdir(options.outDir, { recursive: true });
88
+ await fs.promises.mkdir(resolved.outDir, { recursive: true });
87
89
  }
88
90
  }
89
91
 
90
92
  /* Validate options.version to get the version specific release info */
91
- util.log("info", options.logLevel, "Get version specific release info...");
93
+ util.log("info", resolved.logLevel, "Get version specific release info...");
92
94
  releaseInfo = await util.getReleaseInfo(
93
- options.version,
94
- options.platform,
95
- options.arch,
96
- options.cacheDir,
97
- options.manifestUrl,
95
+ resolved.version,
96
+ resolved.platform,
97
+ resolved.arch,
98
+ resolved.cacheDir,
99
+ resolved.manifestUrl,
98
100
  );
99
101
  util.log(
100
102
  "debug",
101
- options.logLevel,
103
+ resolved.logLevel,
102
104
  `Release info:\n${JSON.stringify(releaseInfo, null, 2)}\n`,
103
105
  );
104
106
 
105
- util.log("info", options.logLevel, "Validate options.* ...");
106
- await util.validate(options, releaseInfo);
107
+ util.log("info", resolved.logLevel, "Validate options.* ...");
108
+ await util.validate(resolved, releaseInfo);
107
109
  util.log(
108
110
  "debug",
109
- options.logLevel,
110
- `Options:\n${JSON.stringify(options, null, 2)}`,
111
+ resolved.logLevel,
112
+ `Options:\n${JSON.stringify(resolved, null, 2)}`,
111
113
  );
112
114
 
113
115
  /* Remove leading "v" from version string */
114
- options.version = releaseInfo.version.slice(1);
116
+ resolved.version = releaseInfo.version.slice(1);
115
117
 
116
- util.log("info", options.logLevel, "Getting NW.js and related binaries...");
118
+ util.log(
119
+ "info",
120
+ resolved.logLevel,
121
+ "Getting NW.js and related binaries...",
122
+ );
117
123
  await get({
118
- version: options.version,
119
- flavor: options.flavor,
120
- platform: options.platform,
121
- arch: options.arch,
122
- downloadUrl: options.downloadUrl,
123
- manifestUrl: options.manifestUrl,
124
- cacheDir: options.cacheDir,
125
- cache: options.cache,
126
- ffmpeg: options.ffmpeg,
127
- nativeAddon: options.nativeAddon,
128
- shaSum: options.shaSum,
129
- logLevel: options.logLevel,
124
+ version: resolved.version,
125
+ flavor: resolved.flavor,
126
+ platform: resolved.platform,
127
+ arch: resolved.arch,
128
+ downloadUrl: /** @type {"https://dl.nwjs.io"} */ (resolved.downloadUrl),
129
+ manifestUrl: /** @type {"https://nwjs.io/versions.json"} */ (
130
+ resolved.manifestUrl
131
+ ),
132
+ cacheDir: resolved.cacheDir,
133
+ cache: resolved.cache,
134
+ ffmpeg: resolved.ffmpeg,
135
+ nativeAddon: resolved.nativeAddon,
136
+ shaSum: resolved.shaSum,
130
137
  });
131
138
 
132
- if (options.mode === "get") {
139
+ if (resolved.mode === "get") {
133
140
  // Do nothing else since we have already downloaded the binaries.
134
141
  return undefined;
135
142
  }
136
143
 
137
- if (options.mode === "run") {
138
- util.log("info", options.logLevel, "Running NW.js in run mode...");
139
- if (options.glob) {
144
+ if (resolved.mode === "run") {
145
+ util.log("info", resolved.logLevel, "Running NW.js in run mode...");
146
+ if (resolved.glob) {
140
147
  throw new Error(
141
148
  "Glob option is not supported when mode is set to run.",
142
149
  );
143
150
  }
144
151
  const nwProcess = await run({
145
- version: options.version,
146
- flavor: options.flavor,
147
- platform: options.platform,
148
- arch: options.arch,
149
- srcDir: options.srcDir,
150
- cacheDir: options.cacheDir,
151
- argv: options.argv,
152
+ version: resolved.version,
153
+ flavor: resolved.flavor,
154
+ platform: resolved.platform,
155
+ arch: resolved.arch,
156
+ srcDir: /** @type {string} */ (resolved.srcDir),
157
+ cacheDir: resolved.cacheDir,
158
+ argv: resolved.argv,
152
159
  });
153
160
  return nwProcess;
154
- } else if (options.mode === "build") {
161
+ } else if (resolved.mode === "build") {
155
162
  util.log(
156
163
  "info",
157
- options.logLevel,
158
- `Build a NW.js application for ${options.platform} ${options.arch}...`,
164
+ resolved.logLevel,
165
+ `Build a NW.js application for ${resolved.platform} ${resolved.arch}...`,
159
166
  );
160
167
  await bld({
161
- version: options.version,
162
- flavor: options.flavor,
163
- platform: options.platform,
164
- arch: options.arch,
165
- manifestUrl: options.manifestUrl,
166
- srcDir: options.srcDir,
167
- cacheDir: options.cacheDir,
168
- outDir: options.outDir,
169
- app: options.app,
170
- glob: options.glob,
171
- managedManifest: options.managedManifest,
172
- zip: options.zip,
168
+ version: resolved.version,
169
+ flavor: resolved.flavor,
170
+ platform: resolved.platform,
171
+ arch: resolved.arch,
172
+ manifestUrl: resolved.manifestUrl,
173
+ srcDir: /** @type {string} */ (resolved.srcDir),
174
+ cacheDir: resolved.cacheDir,
175
+ outDir: resolved.outDir,
176
+ app: /** @type {any} */ (resolved.app),
177
+ glob: resolved.glob,
178
+ managedManifest: resolved.managedManifest,
179
+ zip: resolved.zip,
173
180
  releaseInfo: releaseInfo,
174
181
  });
175
182
  util.log(
176
183
  "info",
177
- options.logLevel,
178
- `Appliction is available at ${path.resolve(options.outDir)}`,
184
+ resolved.logLevel,
185
+ `Appliction is available at ${path.resolve(resolved.outDir)}`,
179
186
  );
180
187
  }
181
188
  } catch (error) {
package/src/util.js CHANGED
@@ -10,7 +10,7 @@ import * as GlobModule from "glob";
10
10
  /**
11
11
  * Get manifest (array of NW release metadata) from URL.
12
12
  * @param {string} manifestUrl Versions manifest URI, https or file path
13
- * @returns {string} - Manifest object
13
+ * @returns {string | Promise<string>} - Manifest object
14
14
  */
15
15
  function getManifest(manifestUrl) {
16
16
  let chunks = "";
@@ -49,7 +49,7 @@ function getManifest(manifestUrl) {
49
49
  * @param {string} arch NW architecture
50
50
  * @param {string} cacheDir Directory to store NW binaries
51
51
  * @param {string} manifestUrl Versions manifest URI, https or file path
52
- * @returns {Promise<object>} Version specific release info
52
+ * @returns {Promise<any>} Version specific release info
53
53
  */
54
54
  async function getReleaseInfo(version, platform, arch, cacheDir, manifestUrl) {
55
55
  let releaseData = undefined;
@@ -66,14 +66,17 @@ async function getReleaseInfo(version, platform, arch, cacheDir, manifestUrl) {
66
66
  await fs.promises.writeFile(manifestPath, data);
67
67
  }
68
68
 
69
- const manifest = JSON.parse(await fs.promises.readFile(manifestPath));
69
+ /** @type {any} */
70
+ const manifest = JSON.parse(
71
+ await fs.promises.readFile(manifestPath, "utf8"),
72
+ );
70
73
  if (version === "latest" || version === "stable" || version === "lts") {
71
74
  // Remove leading "v" from version string
72
75
  version = manifest[version].slice(1);
73
76
  }
74
77
 
75
78
  releaseData = manifest.versions.find(
76
- (release) => release.version === `v${version}`,
79
+ (/** @type {any} */ release) => release.version === `v${version}`,
77
80
  );
78
81
  } catch {
79
82
  console.error(
@@ -108,7 +111,7 @@ const EXE_NAME = {
108
111
  * @param {object} options - glob file options
109
112
  * @param {string | string[]} options.srcDir - app src dir
110
113
  * @param {boolean} options.glob - glob flag
111
- * @returns {Promise<string[]>} - Returns array of file paths
114
+ * @returns {Promise<string[] | string>} - Returns array of file paths, or `srcDir` as-is when `glob` is false
112
115
  */
113
116
  async function globFiles({ srcDir, glob }) {
114
117
  let files;
@@ -143,26 +146,31 @@ async function globFiles({ srcDir, glob }) {
143
146
  * @returns {Promise.<{path: string, json: object}>} - Node manifest
144
147
  */
145
148
  async function getNodeManifest({ srcDir, glob }) {
149
+ /** @type {{path: string, json: any}} */
146
150
  let manifest = {
147
151
  path: "",
148
152
  json: undefined,
149
153
  };
150
154
  let files;
151
155
  if (glob === true) {
152
- files = await globFiles({ srcDir, glob });
156
+ files = /** @type {string[]} */ (await globFiles({ srcDir, glob }));
153
157
  for (const file of files) {
154
158
  if (
155
159
  path.basename(file) === "package.json" &&
156
160
  manifest.json === undefined
157
161
  ) {
158
162
  manifest.path = file;
159
- manifest.json = JSON.parse(await fs.promises.readFile(file));
163
+ manifest.json = JSON.parse(await fs.promises.readFile(file, "utf8"));
160
164
  }
161
165
  }
162
166
  } else {
163
- manifest.path = path.resolve(srcDir, "package.json");
167
+ const srcDirPath = /** @type {string} */ (srcDir);
168
+ manifest.path = path.resolve(srcDirPath, "package.json");
164
169
  manifest.json = JSON.parse(
165
- await fs.promises.readFile(path.resolve(srcDir, "package.json")),
170
+ await fs.promises.readFile(
171
+ path.resolve(srcDirPath, "package.json"),
172
+ "utf8",
173
+ ),
166
174
  );
167
175
  }
168
176
 
@@ -191,9 +199,9 @@ function str2Bool(option) {
191
199
 
192
200
  /**
193
201
  * Parse options.
194
- * @param {import("../../index.js").Options} options Options
195
- * @param {object} pkg Package.json as JSON
196
- * @returns {Promise<object>} Options
202
+ * @param {any} options Options
203
+ * @param {any} pkg Package.json as JSON
204
+ * @returns {Promise<any>} Options
197
205
  */
198
206
  export const parse = async (options, pkg) => {
199
207
  options = options ?? {};
@@ -201,8 +209,12 @@ export const parse = async (options, pkg) => {
201
209
 
202
210
  options.version = options.version ?? "latest";
203
211
  options.flavor = options.flavor ?? "normal";
204
- options.platform = options.platform ?? PLATFORM_KV[process.platform];
205
- options.arch = options.arch ?? ARCH_KV[process.arch];
212
+ options.platform =
213
+ options.platform ??
214
+ PLATFORM_KV[/** @type {"darwin" | "linux" | "win32"} */ (process.platform)];
215
+ options.arch =
216
+ options.arch ??
217
+ ARCH_KV[/** @type {"x64" | "ia32" | "arm64"} */ (process.arch)];
206
218
  options.downloadUrl = options.downloadUrl ?? "https://dl.nwjs.io";
207
219
  options.manifestUrl = options.manifestUrl ?? "https://nwjs.io/versions.json";
208
220
  options.cacheDir = options.cacheDir ?? "./cache";
@@ -307,6 +319,7 @@ export const parse = async (options, pkg) => {
307
319
  options.app.NSHumanReadableCopyright ?? undefined;
308
320
  options.app.NSLocalNetworkUsageDescription =
309
321
  options.app.NSLocalNetworkUsageDescription ?? undefined;
322
+ options.app.LSFileQuarantineEnabled ?? false;
310
323
  }
311
324
 
312
325
  return { ...options };
@@ -314,8 +327,8 @@ export const parse = async (options, pkg) => {
314
327
 
315
328
  /**
316
329
  * Validate options.
317
- * @param {import("../index.js").Options} options Options
318
- * @param {object} releaseInfo Version specific NW release info
330
+ * @param {any} options Options
331
+ * @param {any} releaseInfo Version specific NW release info
319
332
  * @returns {Promise<undefined>} Return undefined if options are valid
320
333
  * @throws {Error} Throw error if options are invalid
321
334
  */
@@ -460,7 +473,8 @@ export const validate = async (options, releaseInfo) => {
460
473
  }
461
474
 
462
475
  if (
463
- (typeof options.zip !== "boolean") & (options.zip !== "zip") &&
476
+ typeof options.zip !== "boolean" &&
477
+ options.zip !== "zip" &&
464
478
  options.zip !== "tar" &&
465
479
  options.zip !== "tgz"
466
480
  ) {
@@ -684,6 +698,15 @@ export const validate = async (options, releaseInfo) => {
684
698
  options.app.NSLocalNetworkUsageDescription,
685
699
  );
686
700
  }
701
+ if (
702
+ options.app.LSFileQuarantineEnabled &&
703
+ typeof options.app.LSFileQuarantineEnabled !== "boolean"
704
+ ) {
705
+ throw new Error(
706
+ "Expected options.app.LSFileQuarantineEnabled to be a boolean. Got " +
707
+ options.app.LSFileQuarantineEnabled,
708
+ );
709
+ }
687
710
  } else {
688
711
  if (typeof options.app.name !== "string") {
689
712
  throw new Error(
@@ -829,8 +852,8 @@ export const validate = async (options, releaseInfo) => {
829
852
  * @async
830
853
  * @function
831
854
  * @param {"chromedriver"} type - NW specific file or directory
832
- * @param {object} options - nwbuild options
833
- * @returns {string} - Path to chromedriver
855
+ * @param {any} options - nwbuild options
856
+ * @returns {Promise<string>} - Path to chromedriver
834
857
  * @throws {Error}
835
858
  */
836
859
  async function getPath(type, options) {
package/src/bld/osx.js DELETED
@@ -1,225 +0,0 @@
1
- import console from "node:console";
2
- import fs from "node:fs";
3
- import path from "node:path";
4
- import process from "node:process";
5
-
6
- import { build, parse } from "plist";
7
- import semver from "semver";
8
-
9
- /**
10
- * Function to update Helper App Plist Files
11
- * @param {string} plistPath - Path to Helper App Plist File
12
- * @param {string} helperName - Helper App Name
13
- * @param {string} helperId - Helper App ID
14
- * @param {string} appCFBundleIdentifier - options.app.CFBundleIdentifier
15
- */
16
- async function updateHelperPlist(
17
- plistPath,
18
- helperName,
19
- helperId,
20
- appCFBundleIdentifier,
21
- ) {
22
- const plistFullPath = path.resolve(plistPath, "Contents/Info.plist");
23
- const plistJson = parse(await fs.promises.readFile(plistFullPath, "utf-8"));
24
- plistJson.CFBundleDisplayName = helperName;
25
- plistJson.CFBundleName = helperName;
26
- plistJson.CFBundleExecutable = helperName;
27
- plistJson.CFBundleIdentifier = `${appCFBundleIdentifier}.${helperId}`;
28
- plistJson.LSFileQuarantineEnabled = false;
29
- await fs.promises.writeFile(plistFullPath, build(plistJson));
30
- }
31
-
32
- /**
33
- *
34
- * @param {object} options - Options.
35
- * @param {string} options.version - NW.js version.
36
- * @param {object} options.app - Application configuration.
37
- * @param {string} options.outDir - Output directory.
38
- * @param {string} options.releaseInfo - Release information.
39
- * @returns {Promise<void>} - Promise.
40
- */
41
- export default async function setOsxConfig({
42
- version,
43
- app,
44
- outDir,
45
- releaseInfo,
46
- }) {
47
- if (process.platform === "win32") {
48
- console.warn(
49
- "MacOS apps built on Windows platform do not preserve all file permissions. See https://github.com/nwutils/nw-builder/issues/716",
50
- );
51
- }
52
-
53
- try {
54
- /**
55
- * @type {string | null}
56
- */
57
- const chromiumVersion = releaseInfo?.components?.chromium;
58
- if (!chromiumVersion) {
59
- throw new Error("Chromium version is missing.");
60
- }
61
- /**
62
- * Path to MacOS application.
63
- * @type {string}
64
- */
65
- const nwjsApp = path.resolve(outDir, "nwjs.app");
66
-
67
- /**
68
- * Path to renamed MacOS application.
69
- * @type {string}
70
- */
71
- const outApp = path.resolve(outDir, `${app.name}.app`);
72
-
73
- /* Rename `nwjs.app` to `${app.name}.app` */
74
- await fs.promises.rename(nwjsApp, outApp);
75
-
76
- /* Rename `Contents/MacOS/nwjs` to `Contents/MacOS/${app.name}` */
77
- await fs.promises.rename(
78
- path.resolve(outApp, "Contents", "MacOS", "nwjs"),
79
- path.resolve(outApp, "Contents", "MacOS", app.name),
80
- );
81
-
82
- /* Rename all Helper apps */
83
- const helperBaseDir = path.resolve(
84
- outApp,
85
- "Contents/Frameworks/nwjs Framework.framework/Versions",
86
- /* TODO: Version mismatch tracked in https://github.com/nwjs/nw.js/issues/8371 */
87
- "Current",
88
- "Helpers",
89
- );
90
-
91
- const helperApps = [
92
- { name: "nwjs Helper (Alerts).app", id: "helper.alert" },
93
- { name: "nwjs Helper (GPU).app", id: "helper.gpu" },
94
- { name: "nwjs Helper (Renderer).app", id: "helper.renderer" },
95
- { name: "nwjs Helper.app", id: "helper" },
96
- ];
97
-
98
- /* MacOS Plugin Helper is removed in NW.js v0.111.0 (Chromium M148) */
99
- if (semver.lt(version, "0.111.0")) {
100
- helperApps.push({
101
- name: "nwjs Helper (Plugin).app",
102
- id: "helper.plugin",
103
- });
104
- }
105
-
106
- for (const helperApp of helperApps) {
107
- const newHelperAppName = helperApp.name.replace(/^nwjs/, app.name);
108
- const oldPath = path.resolve(helperBaseDir, helperApp.name);
109
- const newPath = path.resolve(helperBaseDir, newHelperAppName);
110
-
111
- // Rename Helper base directory
112
- await fs.promises.rename(oldPath, newPath);
113
-
114
- // Rename Helper sub-directory
115
- const helperBaseName = helperApp.name.replace(/.app$/, "");
116
- const subPathBase = path.resolve(newPath, "Contents/MacOS/");
117
- const oldSubPath = path.resolve(subPathBase, helperBaseName);
118
- const newSubPath = path.resolve(
119
- subPathBase,
120
- helperBaseName.replace(/^nwjs/, app.name),
121
- );
122
- await fs.promises.rename(oldSubPath, newSubPath);
123
-
124
- // Update Helper Plist file
125
- await updateHelperPlist(
126
- newPath,
127
- newHelperAppName.replace(/.app$/, ""),
128
- helperApp.id,
129
- app.CFBundleIdentifier,
130
- );
131
- }
132
-
133
- /* Replace default icon with user defined icon if specified. */
134
- if (app.icon !== undefined) {
135
- await fs.promises.copyFile(
136
- path.resolve(app.icon),
137
- path.resolve(outApp, "Contents", "Resources", "app.icns"),
138
- );
139
- }
140
-
141
- /**
142
- * Path to `nwjs.app/Contents/Info.plist`
143
- * @type {string}
144
- */
145
- const contentsInfoPlistPath = path.resolve(
146
- outApp,
147
- "Contents",
148
- "Info.plist",
149
- );
150
-
151
- /**
152
- * Path to `nwjs.app/Contents/Resources/en.lproj/InfoPlist.settings`
153
- * @type {string}
154
- */
155
- const contentsResourcesEnLprojInfoPlistStringsPath = path.resolve(
156
- outApp,
157
- "Contents",
158
- "Resources",
159
- "en.lproj",
160
- "InfoPlist.strings",
161
- );
162
-
163
- /**
164
- * JSON from `nwjs.app/Contents/Info.plist`
165
- * @type {object}
166
- */
167
- const contentsInfoPlistJson = parse(
168
- await fs.promises.readFile(contentsInfoPlistPath, "utf-8"),
169
- );
170
-
171
- /* Update Plist with user defined values. */
172
- contentsInfoPlistJson.LSApplicationCategoryType =
173
- app.LSApplicationCategoryType;
174
- contentsInfoPlistJson.CFBundleIdentifier = app.CFBundleIdentifier;
175
- contentsInfoPlistJson.CFBundleName = app.CFBundleName;
176
- contentsInfoPlistJson.CFBundleDisplayName = app.CFBundleDisplayName;
177
- contentsInfoPlistJson.CFBundleSpokenName = app.CFBundleSpokenName;
178
- contentsInfoPlistJson.CFBundleVersion = app.CFBundleVersion;
179
- contentsInfoPlistJson.CFBundleShortVersionString =
180
- app.CFBundleShortVersionString;
181
- contentsInfoPlistJson.CFBundleExecutable = app.name;
182
- contentsInfoPlistJson.NSLocalNetworkUsageDescription =
183
- app.NSLocalNetworkUsageDescription;
184
- /* Workaround for https://github.com/nwjs/nw.js/issues/7646 */
185
- contentsInfoPlistJson.LSFileQuarantineEnabled = false;
186
-
187
- /* Remove properties that were not updated by the user. */
188
- Object.keys(contentsInfoPlistJson).forEach((option) => {
189
- if (contentsInfoPlistJson[option] === undefined) {
190
- delete contentsInfoPlistJson[option];
191
- }
192
- });
193
-
194
- /**
195
- * Data from `nwjs.app/Contents/Resources/en.lproj/InfoPlist.settings`
196
- * @type {string[]}
197
- */
198
- const contentsResourcesEnLprojInfoPlistStringsArray = (
199
- await fs.promises.readFile(
200
- contentsResourcesEnLprojInfoPlistStringsPath,
201
- "utf-8",
202
- )
203
- ).split("\n");
204
- contentsResourcesEnLprojInfoPlistStringsArray.forEach((line, idx, arr) => {
205
- if (line.includes("NSHumanReadableCopyright")) {
206
- arr[idx] =
207
- `NSHumanReadableCopyright = "${app.NSHumanReadableCopyright}";`;
208
- }
209
- });
210
-
211
- /* Write the updated values to their config files. */
212
- await fs.promises.writeFile(
213
- contentsInfoPlistPath,
214
- build(contentsInfoPlistJson),
215
- );
216
- await fs.promises.writeFile(
217
- contentsResourcesEnLprojInfoPlistStringsPath,
218
- contentsResourcesEnLprojInfoPlistStringsArray
219
- .toString()
220
- .replace(/,/g, "\n"),
221
- );
222
- } catch (error) {
223
- console.error(error);
224
- }
225
- }
package/src/bld.js DELETED
@@ -1,389 +0,0 @@
1
- import child_process from "node:child_process";
2
- import console from "node:console";
3
- import fs from "node:fs";
4
- import path from "node:path";
5
- import process from "node:process";
6
-
7
- import { ZipArchive } from "archiver";
8
- import * as resedit from "resedit";
9
- // pe-library is a direct dependency of resedit
10
- import * as peLibrary from "pe-library";
11
- import * as tar from "tar";
12
-
13
- import util from "./util.js";
14
- import setOsxConfig from "./bld/osx.js";
15
-
16
- /**
17
- * References:
18
- * https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html
19
- * @typedef {object} LinuxRc Linux configuration options
20
- * @property {string} name Name of the application
21
- * @property {string} genericName Generic name of the application
22
- * @property {boolean} noDisplay If true the application is not displayed
23
- * @property {string} comment Tooltip for the entry, for example "View sites on the Internet".
24
- * @property {string} icon Icon to display in file manager, menus, etc.
25
- * @property {boolean} hidden TBD
26
- * @property {string[]} onlyShowIn A list of strings identifying the desktop environments that should (/not) display a given desktop entry
27
- * @property {string[]} notShowIn A list of strings identifying the desktop environments that should (/not) display a given desktop entry
28
- * @property {boolean} dBusActivatable A boolean value specifying if D-Bus activation is supported for this application
29
- * @property {string} tryExec Path to an executable file on disk used to determine if the program is actually installed
30
- * @property {string} exec Program to execute, possibly with arguments.
31
- * @property {string} path If entry is of type Application, the working directory to run the program in.
32
- * @property {boolean} terminal Whether the program runs in a terminal window.
33
- * @property {string[]} actions Identifiers for application actions.
34
- * @property {string[]} mimeType The MIME type(s) supported by this application.
35
- * @property {string[]} categories Categories in which the entry should be shown in a menu
36
- * @property {string[]} implements A list of interfaces that this application implements.
37
- * @property {string[]} keywords A list of strings which may be used in addition to other metadata to describe this entry.
38
- * @property {boolean} startupNotify If true, it is KNOWN that the application will send a "remove" message when started with the DESKTOP_STARTUP_ID environment variable set. If false, it is KNOWN that the application does not work with startup notification at all.
39
- * @property {string} startupWMClass If specified, it is known that the application will map at least one window with the given string as its WM class or WM name hin
40
- * @property {boolean} prefersNonDefaultGPU If true, the application prefers to be run on a more powerful discrete GPU if available.
41
- * @property {string} singleMainWindow If true, the application has a single main window, and does not support having an additional one opened.
42
- */
43
-
44
- /**
45
- * References:
46
- * https://developer.apple.com/documentation/bundleresources/information_property_list
47
- * @typedef {object} OsxRc OSX resource configuration options
48
- * @property {string} name The name of the application
49
- * @property {string} icon The path to the icon file. It should be a .icns file.
50
- * @property {string} LSApplicationCategoryType The category that best describes your app for the App Store.
51
- * @property {string} CFBundleIdentifier A unique identifier for a bundle usually in reverse DNS format.
52
- * @property {string} CFBundleName A user-visible short name for the bundle.
53
- * @property {string} CFBundleDisplayName The user-visible name for the bundle.
54
- * @property {string} CFBundleSpokenName A replacement for the app name in text-to-speech operations.
55
- * @property {string} CFBundleVersion The version of the build that identifies an iteration of the bundle.
56
- * @property {string} CFBundleShortVersionString The release or version number of the bundle.
57
- * @property {string} NSHumanReadableCopyright A human-readable copyright notice for the bundle.
58
- * @property {string} NSLocalNetworkUsageDescription A human-readable description of why the application needs access to the local network.
59
- */
60
-
61
- /**
62
- * References:
63
- * https://learn.microsoft.com/en-us/windows/win32/msi/version
64
- * https://learn.microsoft.com/en-gb/windows/win32/sbscs/application-manifests
65
- * https://learn.microsoft.com/en-us/visualstudio/deployment/trustinfo-element-clickonce-application?view=vs-2022#requestedexecutionlevel
66
- * https://learn.microsoft.com/en-gb/windows/win32/menurc/versioninfo-resource
67
- * @typedef {object} WinRc Windows configuration options. More info
68
- * @property {string} name The name of the application
69
- * @property {string} version The version of the application
70
- * @property {string} comments Additional information that should be displayed for diagnostic purposes.
71
- * @property {string} company Company that produced the file—for example, Microsoft Corporation or Standard Microsystems Corporation, Inc. This string is required.
72
- * @property {string} fileDescription File description to be presented to users. This string may be displayed in a list box when the user is choosing files to install. For example, Keyboard Driver for AT-Style Keyboards. This string is required.
73
- * @property {string} fileVersion Version number of the file. For example, 3.10 or 5.00.RC2. This string is required.
74
- * @property {string} icon The path to the icon file. It should be a .ico file.
75
- * @property {string} internalName Internal name of the file, if one exists—for example, a module name if the file is a dynamic-link library. If the file has no internal name, this string should be the original filename, without extension. This string is required.
76
- * @property {string} legalCopyright Copyright notices that apply to the file. This should include the full text of all notices, legal symbols, copyright dates, and so on. This string is optional.
77
- * @property {string} legalTrademark Trademarks and registered trademarks that apply to the file. This should include the full text of all notices, legal symbols, trademark numbers, and so on. This string is optional.
78
- * @property {string} originalFilename Original name of the file, not including a path. This information enables an application to determine whether a file has been renamed by a user. The format of the name depends on the file system for which the file was created. This string is required.
79
- * @property {string} privateBuild Information about a private version of the file—for example, Built by TESTER1 on \\TESTBED. This string should be present only if VS_FF_PRIVATEBUILD is specified in the fileflags parameter of the root block.
80
- * @property {string} productName Name of the product with which the file is distributed. This string is required.
81
- * @property {string} productVersion Version of the product with which the file is distributed—for example, 3.10 or 5.00.RC2. This string is required.
82
- * @property {string} specialBuild Text that specifies how this version of the file differs from the standard version—for example, Private build for TESTER1 solving mouse problems on M250 and M250E computers. This string should be present only if VS_FF_SPECIALBUILD is specified in the fileflags parameter of the root block.
83
- * @property {string} languageCode Language of the file, defined by Microsoft, see: https://learn.microsoft.com/en-us/openspecs/office_standards/ms-oe376/6c085406-a698-4e12-9d4d-c3b0ee3dbc4a
84
- */
85
-
86
- /**
87
- * @typedef {object} BuildOptions
88
- * @property {string | "latest" | "stable" | "lts"} [version = "latest"] Runtime version
89
- * @property {"normal" | "sdk"} [flavor = "normal"] Build flavor
90
- * @property {"linux" | "osx" | "win"} [platform] Target platform
91
- * @property {"ia32" | "x64" | "arm64"} [arch] Target arch
92
- * @property {string} [manifestUrl = "https://nwjs.io/versions.json"] Versions manifest URI, https or file path
93
- * @property {string} [srcDir = "./src"] Source directory
94
- * @property {string} [cacheDir = "./cache"] Cache directory
95
- * @property {string} [outDir = "./out"] Out directory
96
- * @property {LinuxRc | WinRc | OsxRc} [app] Platform specific rc
97
- * @property {boolean} [glob = true] File globbing
98
- * @property {boolean | string | object} [managedManifest = false] Manage manifest
99
- * @property {false | "zip" | "tar" | "tgz"} [zip = false] Compress built artifacts
100
- * @property {object} [releaseInfo = {}] Version specific release metadata.
101
- */
102
-
103
- /**
104
- * Build NW.js application.
105
- * @async
106
- * @function
107
- * @param {BuildOptions} options - Build options
108
- * @returns {Promise<void>}
109
- */
110
- async function bld({
111
- version = "latest",
112
- flavor = "normal",
113
- platform = util.PLATFORM_KV[process.platform],
114
- arch = util.ARCH_KV[process.arch],
115
- srcDir = "./src",
116
- cacheDir = "./cache",
117
- outDir = "./out",
118
- app,
119
- glob = true,
120
- managedManifest = false,
121
- zip = false,
122
- releaseInfo = {},
123
- }) {
124
- const nwDir = path.resolve(
125
- cacheDir,
126
- `nwjs${flavor === "sdk" ? "-sdk" : ""}-v${version}-${platform}-${arch}`,
127
- );
128
-
129
- await fs.promises.rm(outDir, { force: true, recursive: true });
130
- await fs.promises.cp(nwDir, outDir, {
131
- recursive: true,
132
- verbatimSymlinks: true,
133
- });
134
-
135
- const files = await util.globFiles({ srcDir, glob });
136
-
137
- const nwProjectDir = path.resolve(
138
- outDir,
139
- platform !== "osx" ? "package.nw" : "nwjs.app/Contents/Resources/app.nw",
140
- );
141
-
142
- await fs.promises.mkdir(nwProjectDir, { recursive: true });
143
-
144
- if (glob) {
145
- for (let file of files) {
146
- const stats = await fs.promises.stat(file);
147
- if (stats.isDirectory()) {
148
- continue;
149
- }
150
- await fs.promises.cp(file, path.resolve(nwProjectDir, file), {
151
- recursive: true,
152
- force: true,
153
- });
154
- }
155
- } else {
156
- await fs.promises.cp(files, path.resolve(nwProjectDir), {
157
- recursive: true,
158
- verbatimSymlinks: true,
159
- });
160
- }
161
-
162
- const builtManifest = JSON.parse(
163
- await fs.promises.readFile(
164
- path.resolve(nwProjectDir, "package.json"),
165
- "utf8",
166
- ),
167
- );
168
-
169
- /* Set `product_string` in manifest for MacOS. This is used in renaming the Helper apps. */
170
- if (platform === "osx") {
171
- builtManifest.product_string = app.name;
172
- await fs.promises.writeFile(
173
- path.resolve(nwProjectDir, "package.json"),
174
- JSON.stringify(builtManifest, null, 2),
175
- );
176
- }
177
-
178
- if (
179
- managedManifest === true ||
180
- typeof managedManifest === "object" ||
181
- typeof managedManifest === "string"
182
- ) {
183
- await manageManifest({
184
- nwPkg: builtManifest,
185
- managedManifest,
186
- outDir,
187
- platform,
188
- });
189
- }
190
-
191
- if (platform === "linux") {
192
- await setLinuxConfig({ app, outDir });
193
- } else if (platform === "win") {
194
- await setWinConfig({ app, outDir });
195
- } else if (platform === "osx") {
196
- await setOsxConfig({ version, app, outDir, releaseInfo });
197
- }
198
-
199
- if (zip !== false) {
200
- await compress({ zip, outDir });
201
- }
202
- }
203
-
204
- const manageManifest = async ({ nwPkg, managedManifest, outDir, platform }) => {
205
- let manifest = undefined;
206
-
207
- if (managedManifest === true) {
208
- manifest = nwPkg;
209
- }
210
-
211
- if (typeof managedManifest === "object") {
212
- manifest = managedManifest;
213
- }
214
-
215
- if (typeof managedManifest === "string") {
216
- manifest = JSON.parse(await fs.promises.readFile(managedManifest));
217
- }
218
-
219
- if (manifest.devDependencies) {
220
- manifest.devDependencies = undefined;
221
- }
222
- manifest.packageManager = manifest.packageManager ?? "npm@*";
223
-
224
- await fs.promises.writeFile(
225
- path.resolve(
226
- outDir,
227
- platform !== "osx" ? "package.nw" : "nwjs.app/Contents/Resources/app.nw",
228
- "package.json",
229
- ),
230
- JSON.stringify(manifest, null, 2),
231
- "utf8",
232
- );
233
-
234
- const cwd = path.resolve(
235
- outDir,
236
- platform !== "osx" ? "package.nw" : "nwjs.app/Contents/Resources/app.nw",
237
- );
238
-
239
- if (manifest.packageManager.startsWith("npm")) {
240
- child_process.execSync("npm install", { cwd });
241
- } else if (manifest.packageManager.startsWith("yarn")) {
242
- child_process.execSync("yarn install", { cwd });
243
- } else if (manifest.packageManager.startsWith("pnpm")) {
244
- child_process.execSync("pnpm install", { cwd });
245
- }
246
- };
247
-
248
- const setLinuxConfig = async ({ app, outDir }) => {
249
- if (process.platform === "win32") {
250
- console.warn(
251
- "Linux apps built on Windows platform do not preserve all file permissions. See #716",
252
- );
253
- }
254
- let desktopEntryFile = {
255
- Type: "Application",
256
- Version: "1.5",
257
- Name: app.name,
258
- GenericName: app.genericName,
259
- NoDisplay: app.noDisplay,
260
- Comment: app.comment,
261
- Icon: app.icon ? path.resolve(outDir, "package.nw", app.icon) : "",
262
- Hidden: app.hidden,
263
- OnlyShowIn: app.onlyShowIn,
264
- NotShowIn: app.notShowIn,
265
- DBusActivatable: app.dBusActivatable,
266
- TryExec: app.tryExec,
267
- Exec: app.exec,
268
- Path: app.path,
269
- Terminal: app.terminal,
270
- Actions: app.actions,
271
- MimeType: app.mimeType,
272
- Categories: app.categories,
273
- Implements: app.implements,
274
- Keywords: app.keywords,
275
- StartupNotify: app.startupNotify,
276
- StartupWMClass: app.startupWMClass,
277
- PrefersNonDefaultGPU: app.prefersNonDefaultGPU,
278
- SingleMainWindow: app.singleMainWindow,
279
- };
280
-
281
- await fs.promises.rename(`${outDir}/nw`, `${outDir}/${app.name}`);
282
-
283
- let fileContent = "[Desktop Entry]\n";
284
- Object.keys(desktopEntryFile).forEach((key) => {
285
- if (desktopEntryFile[key] !== undefined) {
286
- fileContent += `${key}=${desktopEntryFile[key]}\n`;
287
- }
288
- });
289
- let filePath = `${outDir}/${app.name}.desktop`;
290
- await fs.promises.writeFile(filePath, fileContent);
291
- };
292
-
293
- const setWinConfig = async ({ app, outDir }) => {
294
- let versionString = {
295
- Comments: app.comments,
296
- CompanyName: app.company,
297
- FileDescription: app.fileDescription,
298
- FileVersion: app.fileVersion,
299
- InternalName: app.internalName,
300
- LegalCopyright: app.legalCopyright,
301
- LegalTrademarks: app.legalTrademark,
302
- OriginalFilename: app.originalFilename,
303
- PrivateBuild: app.privateBuild,
304
- ProductName: app.productName,
305
- ProductVersion: app.productVersion,
306
- SpecialBuild: app.specialBuild,
307
- };
308
-
309
- Object.keys(versionString).forEach((option) => {
310
- if (versionString[option] === undefined) {
311
- delete versionString[option];
312
- }
313
- });
314
-
315
- const outDirAppExe = path.resolve(outDir, `${app.name}.exe`);
316
- await fs.promises.rename(path.resolve(outDir, "nw.exe"), outDirAppExe);
317
- const exe = peLibrary.NtExecutable.from(
318
- await fs.promises.readFile(outDirAppExe),
319
- );
320
- const res = peLibrary.NtExecutableResource.from(exe);
321
- // English (United States)
322
- const EN_US = 1033;
323
- if (app.icon) {
324
- const iconBuffer = await fs.promises.readFile(path.resolve(app.icon));
325
- const iconFile = resedit.Data.IconFile.from(iconBuffer);
326
- const iconGroupIDs = resedit.Resource.IconGroupEntry.fromEntries(
327
- res.entries,
328
- ).map((entry) => entry.id);
329
- resedit.Resource.IconGroupEntry.replaceIconsForResource(
330
- res.entries,
331
- /* Should be `IDR_MAINFRAME` */
332
- iconGroupIDs[0],
333
- EN_US,
334
- iconFile.icons.map((i) => i.data),
335
- );
336
- }
337
- const [vi] = resedit.Resource.VersionInfo.fromEntries(res.entries);
338
- if (app.languageCode !== EN_US) {
339
- res.removeResourceEntry(16, 1, EN_US);
340
- vi.removeAllStringValues({
341
- lang: EN_US,
342
- codepage: 1200,
343
- });
344
- vi.lang = app.languageCode;
345
- }
346
- vi.setFileVersion(app.fileVersion, app.languageCode);
347
- vi.setProductVersion(app.productVersion, app.languageCode);
348
- vi.setStringValues(
349
- {
350
- lang: app.languageCode,
351
- codepage: 1200,
352
- },
353
- versionString,
354
- );
355
- vi.outputToResourceEntries(res.entries);
356
- res.outputResource(exe);
357
- const outBuffer = Buffer.from(exe.generate());
358
- await fs.promises.writeFile(outDirAppExe, outBuffer);
359
- };
360
-
361
- const compress = async ({ zip, outDir }) => {
362
- if (zip === true || zip === "zip") {
363
- const archive = new ZipArchive();
364
- const writeStream = fs.createWriteStream(`${outDir}.zip`);
365
- archive.pipe(writeStream);
366
- archive.directory(outDir, false);
367
- await archive.finalize();
368
- } else if (zip === "tar") {
369
- await tar.create(
370
- {
371
- gzip: false,
372
- file: `${outDir}.tar`,
373
- },
374
- [outDir],
375
- );
376
- } else if (zip === "tgz") {
377
- await tar.create(
378
- {
379
- gzip: true,
380
- file: `${outDir}.tgz`,
381
- },
382
- [outDir],
383
- );
384
- }
385
-
386
- await fs.promises.rm(outDir, { recursive: true, force: true });
387
- };
388
-
389
- export default bld;