nw-builder 4.4.2-beta.2 → 4.4.2-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +2 -1
  2. package/package.json +12 -11
  3. package/src/get.js +149 -152
package/README.md CHANGED
@@ -12,7 +12,8 @@ For version 3, please go to the [corresponding branch](https://github.com/nwutil
12
12
  - Get, run or build applications.
13
13
  - Integrate [FFmpeg community builds](https://github.com/nwjs-ffmpeg-prebuilt/nwjs-ffmpeg-prebuilt)
14
14
  - Configure executable fields and icons
15
- - Support downloading from mirrors
15
+ - Downloading from mirrors
16
+ - Node Native Addon support
16
17
 
17
18
  Check out the [documentation](https://nwutils.io/nw-builder/) if you wish to give `nw-builder` a try.
18
19
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nw-builder",
3
- "version": "4.4.2-beta.2",
3
+ "version": "4.4.2-beta.4",
4
4
  "description": "Build NW.js desktop applications for MacOS, Windows and Linux.",
5
5
  "keywords": [
6
6
  "NW.js",
@@ -42,11 +42,11 @@
42
42
  "url": "https://github.com/nwutils/nw-builder.git"
43
43
  },
44
44
  "scripts": {
45
- "ci:fmt": "prettier --check \"./**/*.{css,html,js,json,md,yml}\"",
46
- "ci:lnt": "eslint --config=.github/eslint.config.cjs .github src test",
47
45
  "fmt": "prettier --write \"./**/*.{css,html,js,json,md,yml}\"",
46
+ "fmt:check": "prettier --check \"./**/*.{css,html,js,json,md,yml}\"",
48
47
  "lnt": "eslint --config=.github/eslint.config.cjs --fix src test",
49
- "doc:dev": "concurrently --kill-others \"node .github/fswatch.config.js\" \"vitepress dev doc\"",
48
+ "lnt:check": "eslint --config=.github/eslint.config.cjs .github src test",
49
+ "doc:dev": "concurrently \"node .github/fswatch.config.js\" \"vitepress dev doc\"",
50
50
  "doc:bld": "node .github/jsdoc.config.cjs && vitepress build doc",
51
51
  "test:unit": "node --test test/unit/index.js",
52
52
  "test:e2e": "node test/e2e/index.js",
@@ -54,26 +54,27 @@
54
54
  },
55
55
  "devDependencies": {
56
56
  "concurrently": "^8.2.1",
57
- "eslint": "^8.51.0",
58
- "eslint-config-tjw-jsdoc": "^1.0.4",
57
+ "eslint": "^8.52.0",
58
+ "eslint-config-tjw-jsdoc": "^1.0.5",
59
59
  "gh-pages": "^6.0.0",
60
60
  "jsdoc": "^4.0.2",
61
61
  "jsdoc-to-markdown": "^8.0.0",
62
62
  "prettier": "^3.0.3",
63
- "selenium-webdriver": "^4.14.0",
64
- "vitepress": "^1.0.0-rc.21"
63
+ "selenium-webdriver": "^4.15.0",
64
+ "vitepress": "^1.0.0-rc.24"
65
65
  },
66
66
  "dependencies": {
67
67
  "cli-progress": "^3.12.0",
68
68
  "compressing": "^1.10.0",
69
69
  "glob": "^10.3.10",
70
- "node-gyp": "^9.4.0",
70
+ "node-gyp": "^10.0.1",
71
71
  "plist": "^3.1.0",
72
72
  "rcedit": "^4.0.0",
73
73
  "winston": "^3.11.0",
74
- "yargs": "^17.7.2"
74
+ "yargs": "^17.7.2",
75
+ "yauzl-promise": "^4.0.0"
75
76
  },
76
- "packageManager": "npm@10.2.0",
77
+ "packageManager": "npm@10.2.3",
77
78
  "engines": {
78
79
  "node": ">=14"
79
80
  }
package/src/get.js CHANGED
@@ -1,21 +1,24 @@
1
- import { exec, spawnSync } from "node:child_process";
1
+ import { exec } from "node:child_process";
2
2
  import { createWriteStream, existsSync } from "node:fs";
3
- import { mkdir, readdir, rename, rm } from "node:fs/promises";
3
+ import { mkdir } from "node:fs/promises";
4
+ import { rename, rm } from "node:fs/promises";
4
5
  import { get as getRequest } from "node:https";
5
6
  import { resolve } from "node:path";
6
- import { arch as ARCH, platform as PLATFORM, exit as EXIT } from "node:process";
7
+ import { arch as ARCH, platform as PLATFORM } from "node:process";
8
+ import { pipeline } from "node:stream";
7
9
 
8
10
  import progress from "cli-progress";
9
11
  import compressing from "compressing";
12
+ import yauzl from "yauzl-promise";
10
13
 
11
14
  import { log } from "./log.js";
12
15
  import { ARCH_KV, PLATFORM_KV, replaceFfmpeg } from "./util.js";
13
16
 
14
17
  /**
15
- * _Note: This an internal function which is not called directly. Please see example usage below._
16
- *
17
18
  * Get binaries.
18
19
  *
20
+ * _Note: This an internal function which is not called directly. Please see example usage below._
21
+ *
19
22
  * @example
20
23
  * // Minimal Usage (uses default values)
21
24
  * nwbuild({
@@ -54,6 +57,13 @@ import { ARCH_KV, PLATFORM_KV, replaceFfmpeg } from "./util.js";
54
57
  * ffmpeg: true,
55
58
  * });
56
59
  *
60
+ * @example
61
+ * // Node headers
62
+ * nwbuild({
63
+ * mode: "get",
64
+ * nativeAddon: "gyp",
65
+ * });
66
+ *
57
67
  * @param {object} options Get mode options
58
68
  * @param {string} options.version NW.js runtime version. Defaults to "latest".
59
69
  * @param {"normal" | "sdk"} options.flavor NW.js build flavor. Defaults to "normal".
@@ -77,7 +87,7 @@ export async function get({
77
87
  ffmpeg = false,
78
88
  nativeAddon = false,
79
89
  }) {
80
- await get_nwjs({
90
+ await getNwjs({
81
91
  version,
82
92
  flavor,
83
93
  platform,
@@ -87,7 +97,7 @@ export async function get({
87
97
  cache,
88
98
  });
89
99
  if (ffmpeg === true) {
90
- await get_ffmpeg({
100
+ await getFfmpeg({
91
101
  version,
92
102
  flavor,
93
103
  platform,
@@ -109,8 +119,6 @@ export async function get({
109
119
  }
110
120
 
111
121
  /**
112
- * Note: This an internal function which is not called directly. Please see example usage below.
113
- *
114
122
  * Get NW.js binaries
115
123
  *
116
124
  * @param {object} options Get mode options
@@ -123,7 +131,7 @@ export async function get({
123
131
  * @param {boolean} options.cache If false, remove cache before download. Defaults to true.
124
132
  * @return {Promise<void>}
125
133
  */
126
- async function get_nwjs({
134
+ async function getNwjs({
127
135
  version = "latest",
128
136
  flavor = "normal",
129
137
  platform = PLATFORM_KV[PLATFORM],
@@ -132,128 +140,132 @@ async function get_nwjs({
132
140
  cacheDir = "./cache",
133
141
  cache = true,
134
142
  }) {
135
- log.debug(`Start NW.js getting binaries`);
136
- let nwCached = true;
137
- const nwDir = resolve(
138
- cacheDir,
139
- `nwjs${flavor === "sdk" ? "-sdk" : ""}-v${version}-${platform}-${arch}`,
140
- );
141
- let out = undefined;
142
- let url = undefined;
143
143
  const bar = new progress.SingleBar({}, progress.Presets.rect);
144
-
145
- // Set download url and destination.
146
- if (
147
- downloadUrl === "https://dl.nwjs.io" ||
148
- downloadUrl === "https://npm.taobao.org/mirrors/nwjs" ||
149
- downloadUrl === "https://npmmirror.com/mirrors/nwjs"
150
- ) {
151
- url = `${downloadUrl}/v${version}/nwjs${
152
- flavor === "sdk" ? "-sdk" : ""
153
- }-v${version}-${platform}-${arch}.${
144
+ const out = resolve(
145
+ cacheDir,
146
+ `nwjs${flavor === "sdk" ? "-sdk" : ""}-v${version}-${platform}-${arch}.${
154
147
  platform === "linux" ? "tar.gz" : "zip"
155
- }`;
156
- out = resolve(cacheDir, `nw.${platform === "linux" ? "tgz" : "zip"}`);
157
- }
158
-
148
+ }`,
149
+ );
159
150
  // If options.cache is false, remove cache.
160
151
  if (cache === false) {
161
- log.debug(`Removing existing NW.js binaries`);
162
- await rm(nwDir, { recursive: true, force: true });
152
+ log.debug(`Removing existing NW.js binaries.`);
153
+ await rm(out, {
154
+ recursive: true,
155
+ force: true,
156
+ });
157
+ log.debug(`Existing NW.js binaries removed.`);
163
158
  }
164
159
 
165
- // Check if cache exists.
166
- try {
167
- await readdir(nwDir);
168
- log.debug(`Found existing NW.js binaries`);
169
- } catch (error) {
170
- log.debug(`No NW.js existing binaries`);
171
- nwCached = false;
160
+ if (existsSync(out) === true) {
161
+ log.debug(`Found existing NW.js binaries.`);
162
+ await rm(
163
+ resolve(
164
+ cacheDir,
165
+ `nwjs${flavor === "sdk" ? "-sdk" : ""}-v${version}-${platform}-${arch}`,
166
+ ),
167
+ { recursive: true, force: true },
168
+ );
169
+ await compressing[platform === "linux" ? "tgz" : "zip"].uncompress(
170
+ out,
171
+ cacheDir,
172
+ );
173
+
174
+ return;
172
175
  }
173
176
 
174
- // If not cached, then download.
175
- if (nwCached === false) {
176
- log.debug(`Downloading NW.js binaries`);
177
- await mkdir(nwDir, { recursive: true });
177
+ const stream = createWriteStream(out);
178
+ const request = new Promise((resolve, reject) => {
179
+ let url = "";
180
+
181
+ // Set download url and destination.
182
+ if (
183
+ downloadUrl === "https://dl.nwjs.io" ||
184
+ downloadUrl === "https://npm.taobao.org/mirrors/nwjs" ||
185
+ downloadUrl === "https://npmmirror.com/mirrors/nwjs"
186
+ ) {
187
+ url = `${downloadUrl}/v${version}/nwjs${
188
+ flavor === "sdk" ? "-sdk" : ""
189
+ }-v${version}-${platform}-${arch}.${
190
+ platform === "linux" ? "tar.gz" : "zip"
191
+ }`;
192
+ }
178
193
 
179
- const stream = createWriteStream(out);
180
- const request = new Promise((resolve, reject) => {
181
- getRequest(url, (response) => {
182
- log.debug(`Response from ${url}`);
183
- // For GitHub releases and mirrors, we need to follow the redirect.
184
- if (
185
- downloadUrl === "https://npm.taobao.org/mirrors/nwjs" ||
186
- downloadUrl === "https://npmmirror.com/mirrors/nwjs"
187
- ) {
188
- url = response.headers.location;
189
- }
194
+ getRequest(url, (response) => {
195
+ // For GitHub releases and mirrors, we need to follow the redirect.
196
+ if (
197
+ downloadUrl === "https://npm.taobao.org/mirrors/nwjs" ||
198
+ downloadUrl === "https://npmmirror.com/mirrors/nwjs"
199
+ ) {
200
+ url = response.headers.location;
201
+ }
190
202
 
191
- getRequest(url, (response) => {
192
- log.debug(`Response from ${url}`);
193
- let chunks = 0;
194
- bar.start(Number(response.headers["content-length"]), 0);
195
- response.on("data", async (chunk) => {
196
- chunks += chunk.length;
197
- bar.increment();
198
- bar.update(chunks);
199
- });
200
-
201
- response.on("error", (error) => {
202
- reject(error);
203
- });
204
-
205
- response.on("end", () => {
206
- log.debug(`Binary fully downloaded`);
207
- bar.stop();
208
- if (platform === "linux") {
209
- compressing.tgz.uncompress(out, cacheDir).then(() => resolve());
210
- } else if (platform === "osx") {
211
- //TODO: compressing package does not restore symlinks on some macOS (eg: circleCI)
212
- const exec = function (cmd) {
213
- log.debug(cmd);
214
- const result = spawnSync(cmd, {
215
- shell: true,
216
- stdio: "inherit",
217
- });
218
- if (result.status !== 0) {
219
- log.debug(`Command failed with status ${result.status}`);
220
- if (result.error) console.log(result.error);
221
- EXIT(1);
222
- }
223
- return resolve();
224
- };
225
- exec(`unzip -o "${out}" -d "${cacheDir}"`);
226
- } else {
227
- compressing.zip.uncompress(out, cacheDir).then(() => resolve());
228
- }
229
- });
230
-
231
- response.pipe(stream);
203
+ getRequest(url, (response) => {
204
+ log.debug(`Downloading from ${url}`);
205
+ let chunks = 0;
206
+ bar.start(Number(response.headers["content-length"]), 0);
207
+ response.on("data", (chunk) => {
208
+ chunks += chunk.length;
209
+ bar.increment();
210
+ bar.update(chunks);
232
211
  });
233
212
 
234
213
  response.on("error", (error) => {
235
214
  reject(error);
236
215
  });
216
+
217
+ response.on("end", () => {
218
+ log.debug(`NW.js download complete.`);
219
+ bar.stop();
220
+ resolve();
221
+ });
222
+
223
+ response.pipe(stream);
237
224
  });
238
- });
239
225
 
240
- // Remove compressed file after download and decompress.
241
- return request.then(async () => {
242
- log.debug(`NW.js binary decompressed starting removal`);
226
+ response.on("error", (error) => {
227
+ reject(error);
228
+ });
229
+ });
230
+ });
243
231
 
244
- await rm(
245
- resolve(cacheDir, `nw.${platform === "linux" ? "tgz" : "zip"}`),
246
- { recursive: true, force: true },
232
+ return request.then(async () => {
233
+ log.debug("Remove existing NW.js before decompression.");
234
+ await rm(
235
+ resolve(
236
+ cacheDir,
237
+ `nwjs${flavor === "sdk" ? "-sdk" : ""}-v${version}-${platform}-${arch}`,
238
+ ),
239
+ { recursive: true, force: true },
240
+ );
241
+ log.debug("Decompress NW.js binaries.");
242
+ if (platform === "osx" && PLATFORM === "darwin") {
243
+ const zip = await yauzl.open(out);
244
+ try {
245
+ for await (const entry of zip) {
246
+ if (entry.filename.endsWith("/")) {
247
+ await mkdir(`${cacheDir}/${entry.filename}`);
248
+ } else {
249
+ const readStream = await entry.openReadStream();
250
+ const writeStream = createWriteStream(
251
+ `${cacheDir}/${entry.filename}`,
252
+ );
253
+ await pipeline(readStream, writeStream);
254
+ }
255
+ }
256
+ } finally {
257
+ await zip.close();
258
+ }
259
+ } else {
260
+ await compressing[platform === "linux" ? "tgz" : "zip"].uncompress(
261
+ out,
262
+ cacheDir,
247
263
  );
248
- log.debug(`NW.js binary zip removed`);
249
- });
250
- }
264
+ }
265
+ });
251
266
  }
252
267
 
253
268
  /**
254
- *
255
- * Note: This an internal function which is not called directly. Please see example usage below.
256
- *
257
269
  * Get FFmpeg binary.
258
270
  *
259
271
  * @param {object} options Get mode options
@@ -265,7 +277,7 @@ async function get_nwjs({
265
277
  * @param {boolean} options.cache If false, remove cache before download. Defaults to true.
266
278
  * @return {Promise<void>}
267
279
  */
268
- async function get_ffmpeg({
280
+ async function getFfmpeg({
269
281
  version = "latest",
270
282
  flavor = "normal",
271
283
  platform = PLATFORM_KV[PLATFORM],
@@ -273,7 +285,6 @@ async function get_ffmpeg({
273
285
  cacheDir = "./cache",
274
286
  cache = true,
275
287
  }) {
276
- log.debug(`Start getting FFmpeg binaries`);
277
288
  const nwDir = resolve(
278
289
  cacheDir,
279
290
  `nwjs${flavor === "sdk" ? "-sdk" : ""}-v${version}-${platform}-${arch}`,
@@ -288,34 +299,32 @@ async function get_ffmpeg({
288
299
 
289
300
  // If options.cache is false, remove cache.
290
301
  if (cache === false) {
291
- log.debug(`Removing existing FFmpeg binaries`);
302
+ log.debug(`Removing existing FFmpeg binary.`);
292
303
  await rm(out, {
293
304
  recursive: true,
294
305
  force: true,
295
306
  });
296
- log.debug(`FFMPEG zip cache removed`);
307
+ log.debug(`Existing FFmpeg binary removed.`);
297
308
  }
298
309
 
299
310
  // Check if cache exists.
300
311
  if (existsSync(out) === true) {
301
- log.debug(`Found existing FFMPEG cache`);
312
+ log.debug(`Found existing FFmpeg binary.`);
302
313
  await compressing.zip.uncompress(out, nwDir);
303
314
  return;
304
315
  }
305
316
 
306
- log.debug(`Downloading FFmpeg binary`);
307
317
  const stream = createWriteStream(out);
308
318
  const request = new Promise((resolve, reject) => {
309
319
  getRequest(url, (response) => {
310
- log.debug(`Response from ${url}`);
311
320
  // For GitHub releases and mirrors, we need to follow the redirect.
312
321
  url = response.headers.location;
313
322
 
314
323
  getRequest(url, (response) => {
315
- log.debug(`Response from ${url}`);
324
+ log.debug(`Downloading from ${url}`);
316
325
  let chunks = 0;
317
326
  bar.start(Number(response.headers["content-length"]), 0);
318
- response.on("data", async (chunk) => {
327
+ response.on("data", (chunk) => {
319
328
  chunks += chunk.length;
320
329
  bar.increment();
321
330
  bar.update(chunks);
@@ -326,7 +335,7 @@ async function get_ffmpeg({
326
335
  });
327
336
 
328
337
  response.on("end", () => {
329
- log.debug(`FFMPEG fully downloaded`);
338
+ log.debug(`FFmpeg download complete.`);
330
339
  bar.stop();
331
340
  resolve();
332
341
  });
@@ -341,19 +350,10 @@ async function get_ffmpeg({
341
350
  });
342
351
 
343
352
  // Remove compressed file after download and decompress.
344
- return request
345
- .then(async () => await compressing.zip.uncompress(out, nwDir))
346
- .then(async () => await replaceFfmpeg(platform, nwDir))
347
- .then(async () => {
348
- if (cache === false) {
349
- log.debug(`Removing FFMPEG zip cache`);
350
- await rm(out, {
351
- recursive: true,
352
- force: true,
353
- });
354
- log.debug(`FFMPEG zip cache removed`);
355
- }
356
- });
353
+ return request.then(async () => {
354
+ await compressing.zip.uncompress(out, nwDir);
355
+ await replaceFfmpeg(platform, nwDir);
356
+ });
357
357
  }
358
358
 
359
359
  /**
@@ -367,11 +367,11 @@ async function get_ffmpeg({
367
367
  * @param {string} options.cache If false, remove cache before download. Defaults to true.
368
368
  * @return {Promise<void>}
369
369
  */
370
- export async function getNodeHeaders({
371
- version,
372
- platform,
373
- arch,
374
- cacheDir,
370
+ async function getNodeHeaders({
371
+ version = "latest",
372
+ platform = PLATFORM_KV[PLATFORM],
373
+ arch = ARCH_KV[ARCH_KV],
374
+ cacheDir = "./cache",
375
375
  cache = true,
376
376
  }) {
377
377
  const bar = new progress.SingleBar({}, progress.Presets.rect);
@@ -382,16 +382,16 @@ export async function getNodeHeaders({
382
382
 
383
383
  // If options.cache is false, remove cache.
384
384
  if (cache === false) {
385
- log.debug(`Removing existing Node headers`);
385
+ log.debug(`Removing existing Node headers.`);
386
386
  await rm(out, {
387
387
  recursive: true,
388
388
  force: true,
389
389
  });
390
- log.debug(`Node headers tgz cache removed`);
390
+ log.debug(`Existing Node headers removed.`);
391
391
  }
392
392
 
393
393
  if (existsSync(out) === true) {
394
- log.debug(`Found existing Node headers cache`);
394
+ log.debug(`Found existing Node headers cache.`);
395
395
  await compressing.tgz.uncompress(out, cacheDir);
396
396
  await rm(resolve(cacheDir, `node-v${version}-${platform}-${arch}`), {
397
397
  recursive: true,
@@ -427,7 +427,7 @@ export async function getNodeHeaders({
427
427
  log.debug(`Response from ${url}`);
428
428
  let chunks = 0;
429
429
  bar.start(Number(response.headers["content-length"]), 0);
430
- response.on("data", async (chunk) => {
430
+ response.on("data", (chunk) => {
431
431
  chunks += chunk.length;
432
432
  bar.increment();
433
433
  bar.update(chunks);
@@ -447,14 +447,11 @@ export async function getNodeHeaders({
447
447
  });
448
448
  });
449
449
 
450
- return request
451
- .then(async () => {
452
- await compressing.tgz.uncompress(out, cacheDir);
453
- })
454
- .then(async () => {
455
- await rename(
456
- resolve(cacheDir, "node"),
457
- resolve(cacheDir, `node-v${version}-${platform}-${arch}`),
458
- );
459
- });
450
+ return request.then(async () => {
451
+ await compressing.tgz.uncompress(out, cacheDir);
452
+ await rename(
453
+ resolve(cacheDir, "node"),
454
+ resolve(cacheDir, `node-v${version}-${platform}-${arch}`),
455
+ );
456
+ });
460
457
  }