nw-builder 4.6.0 → 4.6.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/README.md CHANGED
@@ -107,6 +107,8 @@ nwbuild({
107
107
 
108
108
  ### Run Mode
109
109
 
110
+ > Deprecation warning: From v4.6.0 onward, run mode is deprecated. This logic will be ported over to `nwjs/npm-installer` repo and removed in the next major release.
111
+
110
112
  ```javascript
111
113
  nwbuild({
112
114
  mode: "run",
@@ -270,10 +272,18 @@ nwbuild({
270
272
 
271
273
  ### Maintainer guidelines
272
274
 
275
+ - Approve pull requests before merging.
276
+ - Enforce conventional commits before merging pull requests.
277
+ - A commit's first line should be formatted as `<type>[optional scope]: <description>`.
278
+ - 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. It may include an optional `Notes: ...` section to provide additional context on why the PR is being merged when it doesn't seem like it should.
279
+ - Google's Release Please Action is used to update the changelog, bump the package version and generate GitHub releases.
280
+ - NPM Publish Action publishes to `npm` if there is a version bump.
281
+
273
282
  ## Roadmap
274
283
 
275
284
  ### Bugs
276
285
 
286
+ - Managed Manifest is broken. If glob is disabled and srcDir has no package.json, build fails.
277
287
  - MacOS fails to unzip MacOS NW.js binaries consistently
278
288
  - Add back error, info, warn and debug logs
279
289
 
@@ -290,10 +300,10 @@ nwbuild({
290
300
 
291
301
  ### Chores
292
302
 
293
- - chore(cicd): use `google-github-actions/release-please-action` to automate publishing to npm, updating changelog and creating releases
294
303
  - chore(cli): migrate from `yargs` to `commander`
304
+ - chore(get): investigate [how symlinks are identified](https://github.com/overlookmotel/yauzl-promise/issues/39) and remove the workaround where they are created manually
295
305
  - chore(get): verify sha checksum for downloads
296
- - chore(util): factor out file paths as constant variables
306
+ - chore: annotate file paths as `fs.PathLike` instead of `string`.
297
307
  - chore(bld): factor out core build step
298
308
  - chore(bld): factor out linux config
299
309
  - chore(bld): factor out macos config
@@ -302,6 +312,7 @@ nwbuild({
302
312
  - chore(bld): factor out compressing
303
313
  - chore(bld): factor out managed manifest
304
314
  - chore(bld): move `.desktop` entry file logic to `create-desktop-shortcuts` package
315
+ - chore(util): factor out file paths as constant variables
305
316
 
306
317
  ## FAQ
307
318
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nw-builder",
3
- "version": "4.6.0",
3
+ "version": "4.6.2",
4
4
  "description": "Build NW.js desktop applications for MacOS, Windows and Linux.",
5
5
  "keywords": [
6
6
  "NW.js",
@@ -42,9 +42,10 @@
42
42
  },
43
43
  "scripts": {
44
44
  "lint": "eslint ./src/**/*.js ./test/**/*.js",
45
- "lint:fix": "eslint --fix ./**/*.{js,md} && markdownlint --fix ./README.md",
45
+ "lint:fix": "eslint --fix ./src/**/*.js ./test/**/*.js",
46
+ "markdown:fix": "markdownlint --fix ./README.md",
46
47
  "docs": "jsdoc -d docs ./README.md ./src/index.js ./src/get.js ./src/run.js ./src/bld.js",
47
- "test": "vitest",
48
+ "test": "vitest run",
48
49
  "demo": "cd test/fixture && node demo.js"
49
50
  },
50
51
  "devDependencies": {
@@ -63,7 +64,6 @@
63
64
  "axios": "^1.6.7",
64
65
  "cli-progress": "^3.12.0",
65
66
  "compressing": "^1.10.0",
66
- "fs-extra": "^11.2.0",
67
67
  "glob": "^10.3.10",
68
68
  "node-gyp": "^10.0.1",
69
69
  "plist": "^3.1.0",
package/src/bld.js CHANGED
@@ -83,22 +83,24 @@ import util from "./util.js"
83
83
 
84
84
  /**
85
85
  * @typedef {object} BuildOptions
86
- * @property {string | "latest" | "stable" | "lts"} [options.version = "latest"] Runtime version
87
- * @property {"normal" | "sdk"} [options.flavor = "normal"] Build flavor
88
- * @property {"linux" | "osx" | "win"} [options.platform] Target platform
89
- * @property {"ia32" | "x64" | "arm64"} [options.arch] Target arch
90
- * @property {string} [options.manifestUrl = "https://nwjs.io/versions"] Manifest URL
91
- * @property {string} [options.srcDir = "./src"] Source directory
92
- * @property {string} [options.cacheDir = "./cache"] Cache directory
93
- * @property {string} [options.outDir = "./out"] Out directory
94
- * @property {LinuxRc | WinRc | OsxRc} [options.app] Platform specific rc
95
- * @property {boolean} [options.glob = true] File globbing
96
- * @property {boolean | string | object} [options.managedManifest = false] Manage manifest
97
- * @property {false | "gyp"} [options.nativeAddon = false] Rebuild native modules
98
- * @property {false | "zip" | "tar" | "tgz"} [options.zip = false] Compress built artifacts
86
+ * @property {string | "latest" | "stable" | "lts"} [version = "latest"] Runtime version
87
+ * @property {"normal" | "sdk"} [flavor = "normal"] Build flavor
88
+ * @property {"linux" | "osx" | "win"} [platform] Target platform
89
+ * @property {"ia32" | "x64" | "arm64"} [arch] Target arch
90
+ * @property {string} [manifestUrl = "https://nwjs.io/versions"] Manifest URL
91
+ * @property {string} [srcDir = "./src"] Source directory
92
+ * @property {string} [cacheDir = "./cache"] Cache directory
93
+ * @property {string} [outDir = "./out"] Out directory
94
+ * @property {LinuxRc | WinRc | OsxRc} [app] Platform specific rc
95
+ * @property {boolean} [glob = true] File globbing
96
+ * @property {boolean | string | object} [managedManifest = false] Manage manifest
97
+ * @property {false | "gyp"} [nativeAddon = false] Rebuild native modules
98
+ * @property {false | "zip" | "tar" | "tgz"} [zip = false] Compress built artifacts
99
99
  */
100
100
 
101
101
  /**
102
+ * Build NW.js application.
103
+ *
102
104
  * @async
103
105
  * @function
104
106
  * @param {BuildOptions} options - Build options
@@ -4,7 +4,6 @@ import stream from "node:stream";
4
4
 
5
5
  import tar from "tar";
6
6
  import yauzl from "yauzl-promise";
7
- import {ensureSymlink} from "fs-extra";
8
7
 
9
8
  /**
10
9
  * Decompresses a file at `filePath` to `cacheDir` directory.
@@ -24,73 +23,74 @@ export default async function decompress(filePath, cacheDir) {
24
23
  }
25
24
 
26
25
  /**
27
- * Wrapper for unzipping using `yauzl-promise`.
26
+ * Get file mode from entry. Reference implementation is [here](https://github.com/fpsqdb/zip-lib/blob/ac447d269218d396e05cd7072d0e9cd82b5ec52c/src/unzip.ts#L380).
28
27
  *
29
- * @async
30
- * @function
31
- * @param {string} zippedFile - file path to .zip file
32
- * @param {string} cacheDir - directory to unzip in
33
- * @return {Promise<void>}
28
+ * @param {yauzl.Entry} entry - Yauzl entry
29
+ * @return {number} - entry's file mode
34
30
  */
35
- export async function unzip(zippedFile, cacheDir) {
36
- await unzipInternal(zippedFile, cacheDir, false).then(() => {
37
- unzipInternal(zippedFile, cacheDir, true);
38
- })
31
+ function modeFromEntry(entry) {
32
+ const attr = entry.externalFileAttributes >> 16 || 33188;
33
+
34
+ return [448 /* S_IRWXU */, 56 /* S_IRWXG */, 7 /* S_IRWXO */]
35
+ .map(mask => attr & mask)
36
+ .reduce((a, b) => a + b, attr & 61440 /* S_IFMT */);
39
37
  }
40
38
 
41
39
  /**
42
- * Method for unzip with symlink in theoretical
40
+ * Unzip `zippedFile` to `cacheDir`.
43
41
  *
44
42
  * @async
45
43
  * @function
46
- * @param unzipSymlink
47
- * @param {string} zippedFile - file path to .zip file
48
- * @param {string} cacheDir - directory to unzip in
49
- * @param {boolean} unzipSymlink - Using or not symlink
44
+ * @param {string} zippedFile - file path to .zip file
45
+ * @param {string} cacheDir - directory to unzip in
50
46
  * @return {Promise<void>}
51
47
  */
52
- async function unzipInternal(zippedFile, cacheDir, unzipSymlink ) {
48
+ async function unzip(zippedFile, cacheDir) {
53
49
  const zip = await yauzl.open(zippedFile);
54
-
55
50
  let entry = await zip.readEntry();
51
+ const symlinks = []; // Array to hold symbolic link entries
56
52
 
57
53
  while (entry !== null) {
58
- // console.log(entry)
59
54
  let entryPathAbs = path.join(cacheDir, entry.filename);
60
- // Create the directory beforehand to prevent `ENOENT: no such file or directory` errors.
61
- await fs.promises.mkdir(path.dirname(entryPathAbs), {recursive: true});
62
- const readStream = await entry.openReadStream();
55
+ // Check if entry is a symbolic link
56
+ const isSymlink = ((modeFromEntry(entry) & 0o170000) === 0o120000);
63
57
 
64
- try {
65
- if (!unzipSymlink) {
66
- // Regular method and silent error at this point
58
+ if (isSymlink) {
59
+ // Store symlink entries to process later
60
+ symlinks.push(entry);
61
+ } else {
62
+ // Handle regular files and directories
63
+ await fs.promises.mkdir(path.dirname(entryPathAbs), {recursive: true});
64
+ if (!entry.filename.endsWith('/')) { // Skip directories
65
+ const readStream = await entry.openReadStream();
67
66
  const writeStream = fs.createWriteStream(entryPathAbs);
68
67
  await stream.promises.pipeline(readStream, writeStream);
69
- } else {
70
- // Need check before if file is a symlink or not at this point
71
- const pathContent = await fs.promises.lstat(entryPathAbs);
72
68
 
73
- if (pathContent.isSymbolicLink()) {
74
- const chunks = [];
75
- readStream.on('data', (chunk) => chunks.push(chunk));
76
- await stream.promises.finished(readStream);
77
- // need fetch value of current symlink here
78
- const linkTarget = Buffer.concat(chunks).toString('utf8').trim();
79
- await ensureSymlink(entryPathAbs, path.join(path.dirname(entryPathAbs), linkTarget));
80
- }else{
81
- // Regular method and silent error at this point
82
- const writeStream = fs.createWriteStream(entryPathAbs);
83
- await stream.promises.pipeline(readStream, writeStream);
84
- }
85
- }
86
- } catch (error) {
87
- if (unzipSymlink) {
88
- console.error(error);
69
+ // Set file permissions after the file has been written
70
+ const mode = modeFromEntry(entry);
71
+ await fs.promises.chmod(entryPathAbs, mode);
89
72
  }
90
73
  }
91
74
 
75
+ // Read next entry
92
76
  entry = await zip.readEntry();
93
77
  }
94
78
 
95
- await zip.close();
79
+ // Process symbolic links after all other files have been extracted
80
+ for (const symlinkEntry of symlinks) {
81
+ let entryPathAbs = path.join(cacheDir, symlinkEntry.filename);
82
+ const readStream = await symlinkEntry.openReadStream();
83
+ const chunks = [];
84
+ readStream.on("data", (chunk) => chunks.push(chunk));
85
+ await new Promise(resolve => readStream.on("end", resolve));
86
+ const linkTarget = Buffer.concat(chunks).toString('utf8').trim();
87
+
88
+ // Check if the symlink or a file/directory already exists at the destination
89
+ if (fs.existsSync(entryPathAbs)) {
90
+ //skip
91
+ } else {
92
+ // Create symbolic link
93
+ await fs.promises.symlink(linkTarget, entryPathAbs);
94
+ }
95
+ }
96
96
  }
@@ -0,0 +1,211 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+
4
+ import decompress from "./decompress.js";
5
+ import ffmpeg from "./ffmpeg.js";
6
+ import node from "./node.js";
7
+ import nw from "./nw.js";
8
+
9
+ import util from "../util.js";
10
+
11
+ /**
12
+ * @typedef {object} GetOptions
13
+ * @property {string | "latest" | "stable" | "lts"} [version = "latest"] Runtime version
14
+ * @property {"normal" | "sdk"} [flavor = "normal"] Build flavor
15
+ * @property {"linux" | "osx" | "win"} [platform] Target platform
16
+ * @property {"ia32" | "x64" | "arm64"} [arch] Target arch
17
+ * @property {string} [downloadUrl = "https://dl.nwjs.io"] Download server
18
+ * @property {string} [cacheDir = "./cache"] Cache directory
19
+ * @property {boolean} [cache = true] If false, remove cache and redownload.
20
+ * @property {boolean} [ffmpeg = false] If true, ffmpeg is not downloaded.
21
+ * @property {false | "gyp"} [nativeAddon = false] Rebuild native modules
22
+ */
23
+
24
+ /**
25
+ * Get binaries.
26
+ *
27
+ * @async
28
+ * @function
29
+ * @param {GetOptions} options Get mode options
30
+ * @return {Promise<void>}
31
+ */
32
+ async function get(options) {
33
+
34
+ /**
35
+ * If `options.cacheDir` exists, then `true`. Otherwise, it is `false`.
36
+ *
37
+ * @type {boolean}
38
+ */
39
+ const cacheDirExists = await util.fileExists(options.cacheDir);
40
+ if (cacheDirExists === false) {
41
+ await fs.promises.mkdir(options.cacheDir, { recursive: true });
42
+ }
43
+
44
+ /**
45
+ * File path to compressed binary.
46
+ *
47
+ * @type {string}
48
+ */
49
+ let nwFilePath = path.resolve(
50
+ options.cacheDir,
51
+ `nwjs${options.flavor === "sdk" ? "-sdk" : ""}-v${options.version}-${options.platform}-${options.arch}.${options.platform === "linux" ? "tar.gz" : "zip"
52
+ }`,
53
+ );
54
+
55
+ /**
56
+ * File path to directory which contain NW.js and related binaries.
57
+ *
58
+ * @type {string}
59
+ */
60
+ let nwDirPath = path.resolve(
61
+ options.cacheDir,
62
+ `nwjs${options.flavor === "sdk" ? "-sdk" : ""}-v${options.version}-${options.platform}-${options.arch}`,
63
+ );
64
+
65
+ // If `options.cache` is false, then remove the compressed binary.
66
+ if (options.cache === false) {
67
+ await fs.promises.rm(nwFilePath, {
68
+ recursive: true,
69
+ force: true,
70
+ });
71
+ }
72
+
73
+ // We remove the nwDir to prevent the edge case where you download with ffmpeg flag enabled
74
+ // but want a subsequent build with ffmpeg flag disabled. By removing the directory and
75
+ // decompressing it again, we prevent the community ffmpeg files from being left over.
76
+ // This is important since the community ffmpeg builds have specific licensing constraints.
77
+ await fs.promises.rm(nwDirPath, { recursive: true, force: true });
78
+
79
+ /**
80
+ * If the compressed binary exists, then `true`. Otherwise, it is `false`.
81
+ *
82
+ * @type {boolean}
83
+ */
84
+ const nwFilePathExists = await util.fileExists(nwFilePath);
85
+ if (nwFilePathExists === false) {
86
+ nwFilePath = await nw(options.downloadUrl, options.version, options.flavor, options.platform, options.arch, options.cacheDir);
87
+ }
88
+
89
+ await decompress(nwFilePath, options.cacheDir);
90
+
91
+ if (options.ffmpeg === true) {
92
+
93
+ /**
94
+ * File path to compressed binary which contains community FFmpeg binary.
95
+ *
96
+ * @type {string}
97
+ */
98
+ let ffmpegFilePath = path.resolve(
99
+ options.cacheDir,
100
+ `ffmpeg-${options.version}-${options.platform}-${options.arch}.zip`,
101
+ );
102
+
103
+ // If `options.cache` is false, then remove the compressed binary.
104
+ if (options.cache === false) {
105
+ await fs.promises.rm(ffmpegFilePath, {
106
+ recursive: true,
107
+ force: true,
108
+ });
109
+ }
110
+
111
+ /**
112
+ * If the compressed binary exists, then `true`. Otherwise, it is `false`.
113
+ *
114
+ * @type {boolean}
115
+ */
116
+ const ffmpegFilePathExists = await util.fileExists(ffmpegFilePath);
117
+ if (ffmpegFilePathExists === false) {
118
+ // Do not update the options.downloadUrl with the ffmpeg URL here. Doing so would lead to error when options.ffmpeg and options.nativeAddon are both enabled.
119
+ const downloadUrl =
120
+ "https://github.com/nwjs-ffmpeg-prebuilt/nwjs-ffmpeg-prebuilt/releases/download";
121
+ ffmpegFilePath = await ffmpeg(downloadUrl, options.version, options.platform, options.arch, options.cacheDir);
122
+ }
123
+
124
+ await decompress(ffmpegFilePath, options.cacheDir);
125
+
126
+ /**
127
+ * Platform dependant file name of FFmpeg binary.
128
+ *
129
+ * @type {string}
130
+ */
131
+ let ffmpegFileName = "";
132
+
133
+ if (options.platform === "linux") {
134
+ ffmpegFileName = "libffmpeg.so";
135
+ } else if (options.platform === "win") {
136
+ ffmpegFileName = "ffmpeg.dll";
137
+ } else if (options.platform === "osx") {
138
+ ffmpegFileName = "libffmpeg.dylib";
139
+ }
140
+
141
+ /**
142
+ * File path to platform specific FFmpeg file.
143
+ *
144
+ * @type {string}
145
+ */
146
+ let ffmpegBinaryPath = path.resolve(nwDirPath, ffmpegFileName);
147
+
148
+ /**
149
+ * File path of where FFmpeg will be copied to.
150
+ *
151
+ * @type {string}
152
+ */
153
+ let ffmpegBinaryDest = "";
154
+
155
+ if (options.platform === "linux") {
156
+ ffmpegBinaryDest = path.resolve(nwDirPath, "lib", ffmpegFileName);
157
+ } else if (options.platform === "win") {
158
+ // Extracted file is already in the correct path
159
+ } else if (options.platform === "osx") {
160
+ ffmpegBinaryDest = path.resolve(
161
+ nwDirPath,
162
+ "nwjs.app",
163
+ "Contents",
164
+ "Frameworks",
165
+ "nwjs Framework.framework",
166
+ "Versions",
167
+ "Current",
168
+ ffmpegFileName,
169
+ );
170
+ }
171
+
172
+ await fs.promises.copyFile(ffmpegBinaryPath, ffmpegBinaryDest);
173
+
174
+ }
175
+
176
+ if (options.nativeAddon === "gyp") {
177
+
178
+ /**
179
+ * File path to NW'js Node headers tarball.
180
+ *
181
+ * @type {string}
182
+ */
183
+ let nodeFilePath = path.resolve(
184
+ options.cacheDir,
185
+ `headers-v${options.version}.tar.gz`,
186
+ );
187
+
188
+ // If `options.cache` is false, then remove the compressed binary.
189
+ if (options.cache === false) {
190
+ await fs.promises.rm(nodeFilePath, {
191
+ recursive: true,
192
+ force: true,
193
+ });
194
+ }
195
+
196
+ /**
197
+ * If the compressed binary exists, then `true`. Otherwise, it is `false`.
198
+ *
199
+ * @type {boolean}
200
+ */
201
+ const nodeFilePathExists = await util.fileExists(nodeFilePath);
202
+ if (nodeFilePathExists === false) {
203
+ nodeFilePath = await node(options.downloadUrl, options.version, options.cacheDir);
204
+ }
205
+
206
+ await decompress(nodeFilePath, options.cacheDir);
207
+
208
+ }
209
+ }
210
+
211
+ export default get;
package/src/index.js CHANGED
@@ -3,33 +3,35 @@ import fs from "node:fs";
3
3
  import fsm from "node:fs/promises";
4
4
 
5
5
  import bld from "./bld.js";
6
- import get from "./get.js";
6
+ import get from "./get/index.js";
7
7
  import run from "./run.js";
8
8
  import util from "./util.js";
9
9
 
10
10
  /**
11
11
  * @typedef {object} Options Configuration options
12
- * @property {"./" | string} [srcDir="./"] String of space separated glob patterns which correspond to NW app code
13
- * @property {"get" | "run" | "build"} [mode="build"] Run or build application
14
- * @property {"latest" | "stable" | string} [version="latest"] NW runtime version
15
- * @property {"normal" | "sdk"} [flavor="normal"] NW runtime build flavor
16
- * @property {"linux" | "osx" | "win"} platform NW supported platforms
17
- * @property {"ia32" | "x64" | "arm64"} arch NW supported architectures
12
+ * @property {"get" | "run" | "build"} [mode="build"] Choose between get, run or build mode
13
+ * @property {"latest" | "stable" | string} [version="latest"] Runtime version
14
+ * @property {"normal" | "sdk"} [flavor="normal"] Runtime flavor
15
+ * @property {"linux" | "osx" | "win"} platform Host platform
16
+ * @property {"ia32" | "x64" | "arm64"} arch Host architecture
17
+ * @property {"https://dl.nwjs.io" | string} [downloadUrl="https://dl.nwjs.io"] Download server
18
+ * @property {"https://nwjs.io/versions" | string} [manifestUrl="https://nwjs.io/versions"] Versions manifest
19
+ * @property {"./cache" | string} [cacheDir="./cache"] Directory to cache NW binaries
20
+ * @property {"./" | string} [srcDir="./"] File paths to application code
18
21
  * @property {"./out" | string} [outDir="./out"] Directory to store build artifacts
19
- * @property {"./cache" | string} [cacheDir="./cache"] Directory to store NW binaries
20
- * @property {"https://dl.nwjs.io" | string} [downloadUrl="https://dl.nwjs.io"] URI to download NW binaries from
21
- * @property {"https://nwjs.io/versions" | string} [manifestUrl="https://nwjs.io/versions"] URI to download manifest from
22
+ * @property {boolean | string | object} [managedManifest = false] Managed manifest mode
23
+ * @property {false | "gyp"} [nodeAddon = false] Rebuild Node native addons
22
24
  * @property {object} app Refer to Linux/Windows Specific Options under Getting Started in the docs
23
25
  * @property {boolean} [cache=true] If true the existing cache is used. Otherwise it removes and redownloads it.
24
- * @property {boolean | "zip" | "tar" | "tgz"} [zip=false] If true, "zip", "tar" or "tgz" the outDir directory is compressed.
25
- * @property {boolean} [cli=false] If true the CLI is used to glob srcDir and parse other options
26
26
  * @property {boolean} [ffmpeg=false] If true the chromium ffmpeg is replaced by community version
27
- * @property {boolean} [glob=true] If true globbing is enabled
28
- * @property {"error" | "warn" | "info" | "debug"} [logLevel="info"] Specified log level.
27
+ * @property {boolean} [glob=true] If true file globbing is enabled when parsing srcDir.
28
+ * @property {"error" | "warn" | "info" | "debug"} [logLevel="info"] Specify level of logging.
29
+ * @property {boolean | "zip" | "tar" | "tgz"} [zip=false] If true, "zip", "tar" or "tgz" the outDir directory is compressed.
30
+ * @property {boolean} [cli=false] If true the CLI is used to parse options. This option is used internally.
29
31
  */
30
32
 
31
33
  /**
32
- * Main module exported
34
+ * Main module exported.
33
35
  *
34
36
  * @async
35
37
  * @function
@@ -37,7 +39,7 @@ import util from "./util.js";
37
39
  * @param {Options} options Options
38
40
  * @return {Promise<void>}
39
41
  */
40
- async function nwbuild (options) {
42
+ async function nwbuild(options) {
41
43
  let built;
42
44
  let releaseInfo = {};
43
45
  let manifest = {};
package/src/run.js CHANGED
@@ -7,19 +7,21 @@ import util from "./util.js";
7
7
 
8
8
  /**
9
9
  * @typedef {object} RunOptions
10
- * @property {string | "latest" | "stable" | "lts"} [options.version = "latest"] Runtime version
11
- * @property {"normal" | "sdk"} [options.flavor = "normal"] Build flavor
12
- * @property {"linux" | "osx" | "win"} [options.platform] Target platform
13
- * @property {"ia32" | "x64" | "arm64"} [options.arch] Target arch
14
- * @property {string} [options.srcDir = "./src"] Source directory
15
- * @property {string} [options.cacheDir = "./cache"] Cache directory
16
- * @property {boolean} [options.glob = false] If true, throw error
17
- * @property {string[]} [options.argv = []] CLI arguments
10
+ * @property {string | "latest" | "stable" | "lts"} [version = "latest"] Runtime version
11
+ * @property {"normal" | "sdk"} [flavor = "normal"] Build flavor
12
+ * @property {"linux" | "osx" | "win"} [platform] Target platform
13
+ * @property {"ia32" | "x64" | "arm64"} [arch] Target arch
14
+ * @property {string} [srcDir = "./src"] Source directory
15
+ * @property {string} [cacheDir = "./cache"] Cache directory
16
+ * @property {boolean} [glob = false] If true, throw error
17
+ * @property {string[]} [argv = []] CLI arguments
18
18
  */
19
19
 
20
20
  /**
21
- * Run NW.js application
21
+ * Run NW.js application.
22
22
  *
23
+ * @deprecated since v4.6.0. This logic will be ported over to `nwjs/npm-installer` repo and removed in the next major release (v5.0).
24
+ *
23
25
  * @async
24
26
  * @function
25
27
  *
package/src/util.js CHANGED
@@ -8,7 +8,7 @@ import * as GlobModule from "glob";
8
8
  import semver from "semver";
9
9
 
10
10
  /**
11
- * Get manifest (array of NW release metadata) from URL
11
+ * Get manifest (array of NW release metadata) from URL.
12
12
  *
13
13
  * @param {string} manifestUrl Url to manifest
14
14
  * @return {Promise<object | undefined>} - Manifest object
@@ -39,7 +39,7 @@ function getManifest(manifestUrl) {
39
39
  }
40
40
 
41
41
  /**
42
- * Get version specific release metadata
42
+ * Get version specific release metadata.
43
43
  *
44
44
  * @param {string} version NW version
45
45
  * @param {string} platform NW platform
@@ -105,60 +105,7 @@ const EXE_NAME = {
105
105
  };
106
106
 
107
107
  /**
108
- * Replaces the ffmpeg file in the nwjs directory with the one provided
109
- *
110
- * @param {string} platform The platform to replace the ffmpeg file for
111
- * @param {string} nwDir The directory of the nwjs installation
112
- */
113
- const replaceFfmpeg = async (platform, nwDir) => {
114
- let ffmpegFile;
115
- if (platform === "linux") {
116
- ffmpegFile = "libffmpeg.so";
117
- } else if (platform === "win") {
118
- ffmpegFile = "ffmpeg.dll";
119
- } else if (platform === "osx") {
120
- ffmpegFile = "libffmpeg.dylib";
121
- }
122
- const src = path.resolve(nwDir, ffmpegFile);
123
- if (platform === "linux") {
124
- const dest = path.resolve(nwDir, "lib", ffmpegFile);
125
- await fs.promises.copyFile(src, dest);
126
- } else if (platform === "win") {
127
- // don't do anything for windows because the extracted file is already in the correct path
128
- // await copyFile(src, path.resolve(nwDir, ffmpegFile));
129
- } else if (platform === "osx") {
130
- let dest = path.resolve(
131
- nwDir,
132
- "nwjs.app",
133
- "Contents",
134
- "Frameworks",
135
- "nwjs Framework.framework",
136
- "Versions",
137
- "Current",
138
- ffmpegFile,
139
- );
140
-
141
- try {
142
- await fs.promises.copyFile(src, dest);
143
- } catch (e) {
144
- //some versions of node/macOS complain about destination being a file, and others complain when it is only a directory.
145
- //the only thing I can think to do is to try both
146
- dest = path.resolve(
147
- nwDir,
148
- "nwjs.app",
149
- "Contents",
150
- "Frameworks",
151
- "nwjs Framework.framework",
152
- "Versions",
153
- "Current",
154
- );
155
- await fs.promises.copyFile(src, dest);
156
- }
157
- }
158
- };
159
-
160
- /**
161
- * Glob files
108
+ * Glob files.
162
109
  *
163
110
  * @async
164
111
  * @function
@@ -187,6 +134,8 @@ async function globFiles({
187
134
  }
188
135
 
189
136
  /**
137
+ * Get Node manifest.
138
+ *
190
139
  * @async
191
140
  * @function
192
141
  * @param {object} options - node manifest options
@@ -325,7 +274,7 @@ export const parse = async (options, pkg) => {
325
274
  };
326
275
 
327
276
  /**
328
- * Validate options
277
+ * Validate options.
329
278
  *
330
279
  * @param {import("../index.js").Options} options Options
331
280
  * @param {object} releaseInfo Version specific NW release info
@@ -468,6 +417,7 @@ async function getPath(type, options) {
468
417
  }
469
418
 
470
419
  /**
420
+ * Check if file exists at specified path.
471
421
  *
472
422
  * @param {string} filePath - File path to check existence of
473
423
  * @return {Promise<boolean>} `true` if exists, otherwise `false`
@@ -482,4 +432,4 @@ async function fileExists(filePath) {
482
432
  return exists;
483
433
  }
484
434
 
485
- export default { fileExists, getReleaseInfo, getPath, PLATFORM_KV, ARCH_KV, EXE_NAME, replaceFfmpeg, globFiles, getNodeManifest, parse, validate };
435
+ export default { fileExists, getReleaseInfo, getPath, PLATFORM_KV, ARCH_KV, EXE_NAME, globFiles, getNodeManifest, parse, validate };
package/src/get.js DELETED
@@ -1,232 +0,0 @@
1
- import fs from "node:fs";
2
- import https from "node:https";
3
- import path from "node:path";
4
-
5
- import progress from "cli-progress";
6
- import tar from "tar";
7
-
8
- import decompress, { unzip } from "./get/decompress.js";
9
- import nw from "./get/nw.js";
10
-
11
- import util from "./util.js";
12
-
13
- /**
14
- * @typedef {object} GetOptions
15
- * @property {string | "latest" | "stable" | "lts"} [version = "latest"] Runtime version
16
- * @property {"normal" | "sdk"} [flavor = "normal"] Build flavor
17
- * @property {"linux" | "osx" | "win"} [platform] Target platform
18
- * @property {"ia32" | "x64" | "arm64"} [arch] Target arch
19
- * @property {string} [downloadUrl = "https://dl.nwjs.io"] Download server
20
- * @property {string} [cacheDir = "./cache"] Cache directory
21
- * @property {boolean} [cache = true] If false, remove cache and redownload.
22
- * @property {boolean} [ffmpeg = false] If true, ffmpeg is not downloaded.
23
- * @property {false | "gyp"} [nativeAddon = false] Rebuild native modules
24
- */
25
-
26
- /**
27
- * Get binaries.
28
- *
29
- * @async
30
- * @function
31
- * @param {GetOptions} options Get mode options
32
- * @return {Promise<void>}
33
- */
34
- async function get(options) {
35
-
36
- const cacheDirExists = await util.fileExists(options.cacheDir);
37
- if (cacheDirExists === false) {
38
- await fs.promises.mkdir(options.cacheDir, { recursive: true });
39
- }
40
-
41
- let nwFilePath = path.resolve(
42
- options.cacheDir,
43
- `nwjs${options.flavor === "sdk" ? "-sdk" : ""}-v${options.version}-${options.platform}-${options.arch}.${options.platform === "linux" ? "tar.gz" : "zip"
44
- }`,
45
- );
46
-
47
- let nwDirPath = path.resolve(
48
- options.cacheDir,
49
- `nwjs${options.flavor === "sdk" ? "-sdk" : ""}-v${options.version}-${options.platform}-${options.arch}`,
50
- );
51
-
52
- if (options.cache === false) {
53
- await fs.promises.rm(nwFilePath, {
54
- recursive: true,
55
- force: true,
56
- });
57
- }
58
-
59
- if (util.fileExists(nwFilePath)) {
60
- nwFilePath = await nw(options.downloadUrl, options.version, options.flavor, options.platform, options.arch, options.cacheDir);
61
- }
62
-
63
- await fs.promises.rm(nwDirPath, { recursive: true, force: true });
64
-
65
- await decompress(nwFilePath, options.cacheDir);
66
-
67
- if (options.platform === "osx") {
68
- await createSymlinks(options);
69
- }
70
-
71
- if (options.ffmpeg === true) {
72
- await getFfmpeg(options);
73
- }
74
- if (options.nativeAddon === "gyp") {
75
- await getNodeHeaders(options);
76
- }
77
- }
78
-
79
- const getFfmpeg = async (options) => {
80
- const nwDir = path.resolve(
81
- options.cacheDir,
82
- `nwjs${options.flavor === "sdk" ? "-sdk" : ""}-v${options.version}-${options.platform}-${options.arch}`,
83
- );
84
- const bar = new progress.SingleBar({}, progress.Presets.rect);
85
-
86
- // If options.ffmpeg is true, then download ffmpeg.
87
- options.downloadUrl = "https://github.com/nwjs-ffmpeg-prebuilt/nwjs-ffmpeg-prebuilt/releases/download";
88
- let url = `${options.downloadUrl}/${options.version}/${options.version}-${options.platform}-${options.arch}.zip`;
89
- const out = path.resolve(options.cacheDir, `ffmpeg-v${options.version}-${options.platform}-${options.arch}.zip`);
90
-
91
- // If options.cache is false, remove cache.
92
- if (options.cache === false) {
93
- await fs.promises.rm(out, {
94
- recursive: true,
95
- force: true,
96
- });
97
- }
98
-
99
- // Check if cache exists.
100
- if (fs.existsSync(out) === true) {
101
- await util.unzip(out, nwDir);
102
- return;
103
- }
104
-
105
- const stream = fs.createWriteStream(out);
106
- const request = new Promise((res, rej) => {
107
- https.get(url, (response) => {
108
- // For GitHub releases and mirrors, we need to follow the redirect.
109
- url = response.headers.location;
110
-
111
- https.get(url, (response) => {
112
- let chunks = 0;
113
- bar.start(Number(response.headers["content-length"]), 0);
114
- response.on("data", (chunk) => {
115
- chunks += chunk.length;
116
- bar.increment();
117
- bar.update(chunks);
118
- });
119
-
120
- response.on("error", (error) => {
121
- rej(error);
122
- });
123
-
124
- response.on("end", () => {
125
- bar.stop();
126
- res();
127
- });
128
-
129
- response.pipe(stream);
130
- });
131
-
132
- response.on("error", (error) => {
133
- rej(error);
134
- });
135
- });
136
- });
137
-
138
- // Remove compressed file after download and decompress.
139
- await request;
140
- await unzip(out, nwDir);
141
- await util.replaceFfmpeg(options.platform, nwDir);
142
- }
143
-
144
- const getNodeHeaders = async (options) => {
145
- const bar = new progress.SingleBar({}, progress.Presets.rect);
146
- const out = path.resolve(
147
- options.cacheDir,
148
- `headers-v${options.version}-${options.platform}-${options.arch}.tar.gz`,
149
- );
150
-
151
- // If options.cache is false, remove cache.
152
- if (options.cache === false) {
153
- await fs.promises.rm(out, {
154
- recursive: true,
155
- force: true,
156
- });
157
- }
158
-
159
- if (fs.existsSync(out) === true) {
160
- await tar.extract({
161
- file: out,
162
- C: options.cacheDir
163
- });
164
- await fs.promises.rm(path.resolve(options.cacheDir, `node-v${options.version}-${options.platform}-${options.arch}`), {
165
- recursive: true,
166
- force: true,
167
- });
168
- await fs.promises.rename(
169
- path.resolve(options.cacheDir, "node"),
170
- path.resolve(options.cacheDir, `node-v${options.version}-${options.platform}-${options.arch}`),
171
- );
172
- return;
173
- }
174
-
175
- const stream = fs.createWriteStream(out);
176
- const request = new Promise((res, rej) => {
177
- const url = `${options.downloadUrl}/v${options.version}/nw-headers-v${options.version}.tar.gz`;
178
- https.get(url, (response) => {
179
- let chunks = 0;
180
- bar.start(Number(response.headers["content-length"]), 0);
181
- response.on("data", (chunk) => {
182
- chunks += chunk.length;
183
- bar.increment();
184
- bar.update(chunks);
185
- });
186
-
187
- response.on("error", (error) => {
188
- rej(error);
189
- });
190
-
191
- response.on("end", () => {
192
- bar.stop();
193
- res();
194
- });
195
-
196
- response.pipe(stream);
197
- });
198
- });
199
-
200
- await request;
201
- await tar.extract({
202
- file: out,
203
- C: options.cacheDir
204
- });
205
- await fs.promises.rename(
206
- path.resolve(options.cacheDir, "node"),
207
- path.resolve(options.cacheDir, `node-v${options.version}-${options.platform}-${options.arch}`),
208
- );
209
- }
210
-
211
- const createSymlinks = async (options) => {
212
- let frameworksPath = path.resolve(process.cwd(), options.cacheDir, `nwjs${options.flavor === "sdk" ? "-sdk" : ""}-v${options.version}-${options.platform}-${options.arch}`, "nwjs.app", "Contents", "Frameworks", "nwjs Framework.framework")
213
- // Allow resolve cacheDir from another directory for prevent crash
214
- if (!fs.lstatSync(frameworksPath).isDirectory()) {
215
- frameworksPath = path.resolve(options.cacheDir, `nwjs${options.flavor === "sdk" ? "-sdk" : ""}-v${options.version}-${options.platform}-${options.arch}`, "nwjs.app", "Contents", "Frameworks", "nwjs Framework.framework")
216
- }
217
- const symlinks = [
218
- path.join(frameworksPath, "Helpers"),
219
- path.join(frameworksPath, "Libraries"),
220
- path.join(frameworksPath, "nwjs Framework"),
221
- path.join(frameworksPath, "Resources"),
222
- path.join(frameworksPath, "Versions", "Current"),
223
- ];
224
- for await (const symlink of symlinks) {
225
- const buffer = await fs.promises.readFile(symlink);
226
- const link = buffer.toString();
227
- await fs.promises.rm(symlink);
228
- await fs.promises.symlink(link, symlink);
229
- }
230
- };
231
-
232
- export default get;