nw-builder 4.19.0 → 4.19.2
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 +1 -1
- package/README.md +7 -52
- package/package.json +18 -36
- package/src/cli.js +6 -3
- package/src/index.d.ts +3 -1
- package/src/index.js +78 -71
- package/src/util.js +32 -19
- package/src/bld/osx.js +0 -228
- package/src/bld.js +0 -389
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -329,55 +329,10 @@ nwbuild({
|
|
|
329
329
|
|
|
330
330
|
### External contributor
|
|
331
331
|
|
|
332
|
-
-
|
|
333
|
-
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
-
|
|
338
|
-
-
|
|
339
|
-
|
|
340
|
-
### Maintainer guidelines
|
|
341
|
-
|
|
342
|
-
- Approve pull requests before merging.
|
|
343
|
-
- Enforce conventional commits before merging pull requests.
|
|
344
|
-
- A commit's first line should be formatted as `<type>[optional scope]: <description>`.
|
|
345
|
-
- 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.
|
|
346
|
-
- Google's Release Please Action is used to update the changelog, bump the package version and generate GitHub releases.
|
|
347
|
-
- NPM Publish Action publishes to `npm` if there is a version bump.
|
|
348
|
-
- 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).
|
|
349
|
-
|
|
350
|
-
## Roadmap
|
|
351
|
-
|
|
352
|
-
### Bugs
|
|
353
|
-
|
|
354
|
-
- Managed Manifest is broken. If glob is disabled and srcDir has no package.json, build fails.
|
|
355
|
-
- Add back error, info, warn and debug logs
|
|
356
|
-
|
|
357
|
-
### Features
|
|
358
|
-
|
|
359
|
-
- feat(get): support canary releases
|
|
360
|
-
- feat(pkg): add `AppImage` installer
|
|
361
|
-
- feat(pkg): add `NSIS` installer
|
|
362
|
-
- feat(pkg): add `DMG` installer
|
|
363
|
-
- feat(get): add Linux ARM unofficial support
|
|
364
|
-
- feat(bld): add source code protection
|
|
365
|
-
- feat(pkg): add code signing
|
|
366
|
-
|
|
367
|
-
### Chores
|
|
368
|
-
|
|
369
|
-
- chore(docs): don't store JSDoc definitions in `typedef`s - get's hard to understand during development.
|
|
370
|
-
- chore: annotate file paths as `fs.PathLike` instead of `string`.
|
|
371
|
-
- chore(bld): factor out core build step
|
|
372
|
-
- chore(bld): factor out linux config
|
|
373
|
-
- chore(bld): factor out windows config
|
|
374
|
-
- chore(bld): factor out native addon
|
|
375
|
-
- chore(bld): factor out compressing
|
|
376
|
-
- chore(bld): factor out managed manifest
|
|
377
|
-
- chore(bld): move `.desktop` entry file logic to `create-desktop-shortcuts` package
|
|
378
|
-
|
|
379
|
-
## FAQ
|
|
380
|
-
|
|
381
|
-
## License
|
|
382
|
-
|
|
383
|
-
MIT License.
|
|
332
|
+
- Use Node.js standard libraries whenever possible.
|
|
333
|
+
- Prefer to use syncronous APIs over modern APIs which have been introduced in later versions.
|
|
334
|
+
|
|
335
|
+
### Maintainer
|
|
336
|
+
|
|
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.19.
|
|
3
|
+
"version": "4.19.2",
|
|
4
4
|
"description": "Build NW.js desktop applications for MacOS, Windows and Linux.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"NW.js",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"contributors": [
|
|
22
22
|
{
|
|
23
23
|
"name": "nw-builder Contributors",
|
|
24
|
-
"url": "https://github.com/
|
|
24
|
+
"url": "https://github.com/nwutils/nw-builder/graphs/contributors"
|
|
25
25
|
}
|
|
26
26
|
],
|
|
27
27
|
"license": "MIT",
|
|
@@ -38,50 +38,32 @@
|
|
|
38
38
|
"homepage": "https://github.com/nwutils/nw-builder",
|
|
39
39
|
"repository": {
|
|
40
40
|
"type": "git",
|
|
41
|
-
"url": "https://github.com/nwutils/nw-builder.git"
|
|
41
|
+
"url": "git+https://github.com/nwutils/nw-builder.git",
|
|
42
|
+
"directory": "./packages/nw-builder"
|
|
42
43
|
},
|
|
43
44
|
"scripts": {
|
|
44
45
|
"postinstall": "node ./src/postinstall.js",
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"demo:bld:
|
|
52
|
-
"demo:
|
|
53
|
-
"demo:
|
|
54
|
-
"demo:exe:
|
|
55
|
-
"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"
|
|
46
|
+
"lint": "eslint ./src ../../tests/specs/nw-builder",
|
|
47
|
+
"lint:fix": "eslint --fix ./src ../../tests/specs/nw-builder",
|
|
48
|
+
"type": "tsc",
|
|
49
|
+
"test": "node --test \"../../tests/specs/nw-builder/*.test.js\"",
|
|
50
|
+
"demo:bld:linux": "node ../../tests/fixtures/nw-builder/demo.linux.js",
|
|
51
|
+
"demo:bld:osx": "node ../../tests/fixtures/nw-builder/demo.osx.js",
|
|
52
|
+
"demo:bld:win": "node ../../tests/fixtures/nw-builder/demo.win.js",
|
|
53
|
+
"demo:exe:linux": "../../tests/fixtures/nw-builder/out/linux/Demo",
|
|
54
|
+
"demo:exe:osx": "../../tests/fixtures/nw-builder/out/osx/Demo.app/Contents/MacOS/Demo",
|
|
55
|
+
"demo:exe:win": "../../tests/fixtures/nw-builder/out/win/Demo.exe",
|
|
56
|
+
"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
57
|
},
|
|
59
58
|
"devDependencies": {
|
|
60
|
-
"@eslint/js": "^10.0.1",
|
|
61
|
-
"@vitest/coverage-v8": "^4.1.11",
|
|
62
59
|
"base-volta-off-of-nwjs": "^1.0.5",
|
|
63
|
-
"
|
|
64
|
-
"eslint-plugin-jsdoc": "^64.2.1",
|
|
65
|
-
"eslint-plugin-simple-import-sort": "^14.0.0",
|
|
66
|
-
"globals": "^17.11.0",
|
|
67
|
-
"nw": "^0.115.0",
|
|
68
|
-
"prettier": "^3.9.6",
|
|
69
|
-
"selenium-webdriver": "^4.47.0",
|
|
70
|
-
"vitest": "^4.1.11"
|
|
60
|
+
"nw": "^0.115.0"
|
|
71
61
|
},
|
|
72
62
|
"dependencies": {
|
|
63
|
+
"@nwutils/builder": "^0.1.0",
|
|
73
64
|
"@nwutils/getter": "^0.2.5",
|
|
74
65
|
"@nwutils/runner": "^0.2.3",
|
|
75
|
-
"archiver": "^8.0.0",
|
|
76
66
|
"commander": "^15.0.0",
|
|
77
|
-
"glob": "^13.0.6"
|
|
78
|
-
"plist": "^5.0.0",
|
|
79
|
-
"resedit": "^3.1.0",
|
|
80
|
-
"semver": "^7.8.5",
|
|
81
|
-
"tar": "^7.5.22"
|
|
82
|
-
},
|
|
83
|
-
"volta": {
|
|
84
|
-
"node": "26.7.0",
|
|
85
|
-
"npm": "11.13.0"
|
|
67
|
+
"glob": "^13.0.6"
|
|
86
68
|
}
|
|
87
69
|
}
|
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[
|
|
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
|
|
|
@@ -181,6 +183,6 @@ export interface OsxAppOptions {
|
|
|
181
183
|
*/
|
|
182
184
|
declare function nwbuild<P extends SupportedPlatform>(
|
|
183
185
|
options: Options<P>,
|
|
184
|
-
): Promise<
|
|
186
|
+
): Promise<ChildProcess | null | undefined>;
|
|
185
187
|
|
|
186
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
|
|
18
|
-
* @property {"ia32" | "x64" | "arm64"} arch
|
|
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 {
|
|
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
|
|
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} [
|
|
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
|
-
|
|
72
|
+
/** @type {Required<Options>} */
|
|
73
|
+
const resolved = await util.parse(options, manifest.json);
|
|
71
74
|
util.log(
|
|
72
75
|
"debug",
|
|
73
|
-
|
|
74
|
-
|
|
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(
|
|
80
|
+
built = fs.existsSync(resolved.cacheDir);
|
|
79
81
|
if (built === false) {
|
|
80
|
-
await fs.promises.mkdir(
|
|
82
|
+
await fs.promises.mkdir(resolved.cacheDir, { recursive: true });
|
|
81
83
|
}
|
|
82
84
|
|
|
83
|
-
if (
|
|
84
|
-
built = fs.existsSync(
|
|
85
|
+
if (resolved.mode === "build") {
|
|
86
|
+
built = fs.existsSync(resolved.outDir);
|
|
85
87
|
if (built === false) {
|
|
86
|
-
await fs.promises.mkdir(
|
|
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",
|
|
93
|
+
util.log("info", resolved.logLevel, "Get version specific release info...");
|
|
92
94
|
releaseInfo = await util.getReleaseInfo(
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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
|
-
|
|
103
|
+
resolved.logLevel,
|
|
102
104
|
`Release info:\n${JSON.stringify(releaseInfo, null, 2)}\n`,
|
|
103
105
|
);
|
|
104
106
|
|
|
105
|
-
util.log("info",
|
|
106
|
-
await util.validate(
|
|
107
|
+
util.log("info", resolved.logLevel, "Validate options.* ...");
|
|
108
|
+
await util.validate(resolved, releaseInfo);
|
|
107
109
|
util.log(
|
|
108
110
|
"debug",
|
|
109
|
-
|
|
110
|
-
`Options:\n${JSON.stringify(
|
|
111
|
+
resolved.logLevel,
|
|
112
|
+
`Options:\n${JSON.stringify(resolved, null, 2)}`,
|
|
111
113
|
);
|
|
112
114
|
|
|
113
115
|
/* Remove leading "v" from version string */
|
|
114
|
-
|
|
116
|
+
resolved.version = releaseInfo.version.slice(1);
|
|
115
117
|
|
|
116
|
-
util.log(
|
|
118
|
+
util.log(
|
|
119
|
+
"info",
|
|
120
|
+
resolved.logLevel,
|
|
121
|
+
"Getting NW.js and related binaries...",
|
|
122
|
+
);
|
|
117
123
|
await get({
|
|
118
|
-
version:
|
|
119
|
-
flavor:
|
|
120
|
-
platform:
|
|
121
|
-
arch:
|
|
122
|
-
downloadUrl:
|
|
123
|
-
manifestUrl:
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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 (
|
|
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 (
|
|
138
|
-
util.log("info",
|
|
139
|
-
if (
|
|
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:
|
|
146
|
-
flavor:
|
|
147
|
-
platform:
|
|
148
|
-
arch:
|
|
149
|
-
srcDir:
|
|
150
|
-
cacheDir:
|
|
151
|
-
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 (
|
|
161
|
+
} else if (resolved.mode === "build") {
|
|
155
162
|
util.log(
|
|
156
163
|
"info",
|
|
157
|
-
|
|
158
|
-
`Build a NW.js application for ${
|
|
164
|
+
resolved.logLevel,
|
|
165
|
+
`Build a NW.js application for ${resolved.platform} ${resolved.arch}...`,
|
|
159
166
|
);
|
|
160
167
|
await bld({
|
|
161
|
-
version:
|
|
162
|
-
flavor:
|
|
163
|
-
platform:
|
|
164
|
-
arch:
|
|
165
|
-
manifestUrl:
|
|
166
|
-
srcDir:
|
|
167
|
-
cacheDir:
|
|
168
|
-
outDir:
|
|
169
|
-
app:
|
|
170
|
-
glob:
|
|
171
|
-
managedManifest:
|
|
172
|
-
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
|
-
|
|
178
|
-
`Appliction is available at ${path.resolve(
|
|
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<
|
|
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
|
-
|
|
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[]>}
|
|
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
|
-
|
|
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(
|
|
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 {
|
|
195
|
-
* @param {
|
|
196
|
-
* @returns {Promise<
|
|
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 =
|
|
205
|
-
|
|
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";
|
|
@@ -315,8 +327,8 @@ export const parse = async (options, pkg) => {
|
|
|
315
327
|
|
|
316
328
|
/**
|
|
317
329
|
* Validate options.
|
|
318
|
-
* @param {
|
|
319
|
-
* @param {
|
|
330
|
+
* @param {any} options Options
|
|
331
|
+
* @param {any} releaseInfo Version specific NW release info
|
|
320
332
|
* @returns {Promise<undefined>} Return undefined if options are valid
|
|
321
333
|
* @throws {Error} Throw error if options are invalid
|
|
322
334
|
*/
|
|
@@ -461,7 +473,8 @@ export const validate = async (options, releaseInfo) => {
|
|
|
461
473
|
}
|
|
462
474
|
|
|
463
475
|
if (
|
|
464
|
-
|
|
476
|
+
typeof options.zip !== "boolean" &&
|
|
477
|
+
options.zip !== "zip" &&
|
|
465
478
|
options.zip !== "tar" &&
|
|
466
479
|
options.zip !== "tgz"
|
|
467
480
|
) {
|
|
@@ -839,8 +852,8 @@ export const validate = async (options, releaseInfo) => {
|
|
|
839
852
|
* @async
|
|
840
853
|
* @function
|
|
841
854
|
* @param {"chromedriver"} type - NW specific file or directory
|
|
842
|
-
* @param {
|
|
843
|
-
* @returns {string}
|
|
855
|
+
* @param {any} options - nwbuild options
|
|
856
|
+
* @returns {Promise<string>} - Path to chromedriver
|
|
844
857
|
* @throws {Error}
|
|
845
858
|
*/
|
|
846
859
|
async function getPath(type, options) {
|
package/src/bld/osx.js
DELETED
|
@@ -1,228 +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
|
-
* @param {string} LSFileQuarantineEnabled - options.app.LSFileQuarantineEnabled
|
|
16
|
-
*/
|
|
17
|
-
async function updateHelperPlist(
|
|
18
|
-
plistPath,
|
|
19
|
-
helperName,
|
|
20
|
-
helperId,
|
|
21
|
-
appCFBundleIdentifier,
|
|
22
|
-
LSFileQuarantineEnabled,
|
|
23
|
-
) {
|
|
24
|
-
const plistFullPath = path.resolve(plistPath, "Contents/Info.plist");
|
|
25
|
-
const plistJson = parse(await fs.promises.readFile(plistFullPath, "utf-8"));
|
|
26
|
-
plistJson.CFBundleDisplayName = helperName;
|
|
27
|
-
plistJson.CFBundleName = helperName;
|
|
28
|
-
plistJson.CFBundleExecutable = helperName;
|
|
29
|
-
plistJson.CFBundleIdentifier = `${appCFBundleIdentifier}.${helperId}`;
|
|
30
|
-
plistJson.LSFileQuarantineEnabled = LSFileQuarantineEnabled;
|
|
31
|
-
await fs.promises.writeFile(plistFullPath, build(plistJson));
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
*
|
|
36
|
-
* @param {object} options - Options.
|
|
37
|
-
* @param {string} options.version - NW.js version.
|
|
38
|
-
* @param {object} options.app - Application configuration.
|
|
39
|
-
* @param {string} options.outDir - Output directory.
|
|
40
|
-
* @param {string} options.releaseInfo - Release information.
|
|
41
|
-
* @returns {Promise<void>} - Promise.
|
|
42
|
-
*/
|
|
43
|
-
export default async function setOsxConfig({
|
|
44
|
-
version,
|
|
45
|
-
app,
|
|
46
|
-
outDir,
|
|
47
|
-
releaseInfo,
|
|
48
|
-
}) {
|
|
49
|
-
if (process.platform === "win32") {
|
|
50
|
-
console.warn(
|
|
51
|
-
"MacOS apps built on Windows platform do not preserve all file permissions. See https://github.com/nwutils/nw-builder/issues/716",
|
|
52
|
-
);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
try {
|
|
56
|
-
/**
|
|
57
|
-
* @type {string | null}
|
|
58
|
-
*/
|
|
59
|
-
const chromiumVersion = releaseInfo?.components?.chromium;
|
|
60
|
-
if (!chromiumVersion) {
|
|
61
|
-
throw new Error("Chromium version is missing.");
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Path to MacOS application.
|
|
65
|
-
* @type {string}
|
|
66
|
-
*/
|
|
67
|
-
const nwjsApp = path.resolve(outDir, "nwjs.app");
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Path to renamed MacOS application.
|
|
71
|
-
* @type {string}
|
|
72
|
-
*/
|
|
73
|
-
const outApp = path.resolve(outDir, `${app.name}.app`);
|
|
74
|
-
|
|
75
|
-
/* Rename `nwjs.app` to `${app.name}.app` */
|
|
76
|
-
await fs.promises.rename(nwjsApp, outApp);
|
|
77
|
-
|
|
78
|
-
/* Rename `Contents/MacOS/nwjs` to `Contents/MacOS/${app.name}` */
|
|
79
|
-
await fs.promises.rename(
|
|
80
|
-
path.resolve(outApp, "Contents", "MacOS", "nwjs"),
|
|
81
|
-
path.resolve(outApp, "Contents", "MacOS", app.name),
|
|
82
|
-
);
|
|
83
|
-
|
|
84
|
-
/* Rename all Helper apps */
|
|
85
|
-
const helperBaseDir = path.resolve(
|
|
86
|
-
outApp,
|
|
87
|
-
"Contents/Frameworks/nwjs Framework.framework/Versions",
|
|
88
|
-
/* TODO: Version mismatch tracked in https://github.com/nwjs/nw.js/issues/8371 */
|
|
89
|
-
"Current",
|
|
90
|
-
"Helpers",
|
|
91
|
-
);
|
|
92
|
-
|
|
93
|
-
const helperApps = [
|
|
94
|
-
{ name: "nwjs Helper (Alerts).app", id: "helper.alert" },
|
|
95
|
-
{ name: "nwjs Helper (GPU).app", id: "helper.gpu" },
|
|
96
|
-
{ name: "nwjs Helper (Renderer).app", id: "helper.renderer" },
|
|
97
|
-
{ name: "nwjs Helper.app", id: "helper" },
|
|
98
|
-
];
|
|
99
|
-
|
|
100
|
-
/* MacOS Plugin Helper is removed in NW.js v0.111.0 (Chromium M148) */
|
|
101
|
-
if (semver.lt(version, "0.111.0")) {
|
|
102
|
-
helperApps.push({
|
|
103
|
-
name: "nwjs Helper (Plugin).app",
|
|
104
|
-
id: "helper.plugin",
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
for (const helperApp of helperApps) {
|
|
109
|
-
const newHelperAppName = helperApp.name.replace(/^nwjs/, app.name);
|
|
110
|
-
const oldPath = path.resolve(helperBaseDir, helperApp.name);
|
|
111
|
-
const newPath = path.resolve(helperBaseDir, newHelperAppName);
|
|
112
|
-
|
|
113
|
-
// Rename Helper base directory
|
|
114
|
-
await fs.promises.rename(oldPath, newPath);
|
|
115
|
-
|
|
116
|
-
// Rename Helper sub-directory
|
|
117
|
-
const helperBaseName = helperApp.name.replace(/.app$/, "");
|
|
118
|
-
const subPathBase = path.resolve(newPath, "Contents/MacOS/");
|
|
119
|
-
const oldSubPath = path.resolve(subPathBase, helperBaseName);
|
|
120
|
-
const newSubPath = path.resolve(
|
|
121
|
-
subPathBase,
|
|
122
|
-
helperBaseName.replace(/^nwjs/, app.name),
|
|
123
|
-
);
|
|
124
|
-
await fs.promises.rename(oldSubPath, newSubPath);
|
|
125
|
-
|
|
126
|
-
// Update Helper Plist file
|
|
127
|
-
await updateHelperPlist(
|
|
128
|
-
newPath,
|
|
129
|
-
newHelperAppName.replace(/.app$/, ""),
|
|
130
|
-
helperApp.id,
|
|
131
|
-
app.CFBundleIdentifier,
|
|
132
|
-
app.LSFileQuarantineEnabled,
|
|
133
|
-
);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/* Replace default icon with user defined icon if specified. */
|
|
137
|
-
if (app.icon !== undefined) {
|
|
138
|
-
await fs.promises.copyFile(
|
|
139
|
-
path.resolve(app.icon),
|
|
140
|
-
path.resolve(outApp, "Contents", "Resources", "app.icns"),
|
|
141
|
-
);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Path to `nwjs.app/Contents/Info.plist`
|
|
146
|
-
* @type {string}
|
|
147
|
-
*/
|
|
148
|
-
const contentsInfoPlistPath = path.resolve(
|
|
149
|
-
outApp,
|
|
150
|
-
"Contents",
|
|
151
|
-
"Info.plist",
|
|
152
|
-
);
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* Path to `nwjs.app/Contents/Resources/en.lproj/InfoPlist.settings`
|
|
156
|
-
* @type {string}
|
|
157
|
-
*/
|
|
158
|
-
const contentsResourcesEnLprojInfoPlistStringsPath = path.resolve(
|
|
159
|
-
outApp,
|
|
160
|
-
"Contents",
|
|
161
|
-
"Resources",
|
|
162
|
-
"en.lproj",
|
|
163
|
-
"InfoPlist.strings",
|
|
164
|
-
);
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* JSON from `nwjs.app/Contents/Info.plist`
|
|
168
|
-
* @type {object}
|
|
169
|
-
*/
|
|
170
|
-
const contentsInfoPlistJson = parse(
|
|
171
|
-
await fs.promises.readFile(contentsInfoPlistPath, "utf-8"),
|
|
172
|
-
);
|
|
173
|
-
|
|
174
|
-
/* Update Plist with user defined values. */
|
|
175
|
-
contentsInfoPlistJson.LSApplicationCategoryType =
|
|
176
|
-
app.LSApplicationCategoryType;
|
|
177
|
-
contentsInfoPlistJson.CFBundleIdentifier = app.CFBundleIdentifier;
|
|
178
|
-
contentsInfoPlistJson.CFBundleName = app.CFBundleName;
|
|
179
|
-
contentsInfoPlistJson.CFBundleDisplayName = app.CFBundleDisplayName;
|
|
180
|
-
contentsInfoPlistJson.CFBundleSpokenName = app.CFBundleSpokenName;
|
|
181
|
-
contentsInfoPlistJson.CFBundleVersion = app.CFBundleVersion;
|
|
182
|
-
contentsInfoPlistJson.CFBundleShortVersionString =
|
|
183
|
-
app.CFBundleShortVersionString;
|
|
184
|
-
contentsInfoPlistJson.CFBundleExecutable = app.name;
|
|
185
|
-
contentsInfoPlistJson.NSLocalNetworkUsageDescription =
|
|
186
|
-
app.NSLocalNetworkUsageDescription;
|
|
187
|
-
/* See https://github.com/nwjs/nw.js/issues/7646 */
|
|
188
|
-
contentsInfoPlistJson.LSFileQuarantineEnabled = app.LSFileQuarantineEnabled;
|
|
189
|
-
|
|
190
|
-
/* Remove properties that were not updated by the user. */
|
|
191
|
-
Object.keys(contentsInfoPlistJson).forEach((option) => {
|
|
192
|
-
if (contentsInfoPlistJson[option] === undefined) {
|
|
193
|
-
delete contentsInfoPlistJson[option];
|
|
194
|
-
}
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* Data from `nwjs.app/Contents/Resources/en.lproj/InfoPlist.settings`
|
|
199
|
-
* @type {string[]}
|
|
200
|
-
*/
|
|
201
|
-
const contentsResourcesEnLprojInfoPlistStringsArray = (
|
|
202
|
-
await fs.promises.readFile(
|
|
203
|
-
contentsResourcesEnLprojInfoPlistStringsPath,
|
|
204
|
-
"utf-8",
|
|
205
|
-
)
|
|
206
|
-
).split("\n");
|
|
207
|
-
contentsResourcesEnLprojInfoPlistStringsArray.forEach((line, idx, arr) => {
|
|
208
|
-
if (line.includes("NSHumanReadableCopyright")) {
|
|
209
|
-
arr[idx] =
|
|
210
|
-
`NSHumanReadableCopyright = "${app.NSHumanReadableCopyright}";`;
|
|
211
|
-
}
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
/* Write the updated values to their config files. */
|
|
215
|
-
await fs.promises.writeFile(
|
|
216
|
-
contentsInfoPlistPath,
|
|
217
|
-
build(contentsInfoPlistJson),
|
|
218
|
-
);
|
|
219
|
-
await fs.promises.writeFile(
|
|
220
|
-
contentsResourcesEnLprojInfoPlistStringsPath,
|
|
221
|
-
contentsResourcesEnLprojInfoPlistStringsArray
|
|
222
|
-
.toString()
|
|
223
|
-
.replace(/,/g, "\n"),
|
|
224
|
-
);
|
|
225
|
-
} catch (error) {
|
|
226
|
-
console.error(error);
|
|
227
|
-
}
|
|
228
|
-
}
|
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;
|