electron-incremental-update 0.9.1 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/vite.js CHANGED
@@ -30,14 +30,21 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/vite.ts
31
31
  var vite_exports = {};
32
32
  __export(vite_exports, {
33
- ElectronUpdater: () => ElectronUpdater
33
+ debugStartup: () => debugStartup,
34
+ electronWithUpdater: () => electronWithUpdater
34
35
  });
35
36
  module.exports = __toCommonJS(vite_exports);
37
+ var import_node_path3 = require("path");
38
+ var import_node_fs4 = require("fs");
36
39
  var import_vite = require("vite");
40
+ var import_simple = __toESM(require("vite-plugin-electron/simple"));
41
+ var import_vite_plugin_electron = require("vite-plugin-electron");
42
+ var import_plugin = require("vite-plugin-electron/plugin");
37
43
 
38
44
  // src/build-plugins/build.ts
39
45
  var import_promises = require("fs/promises");
40
46
  var import_node_fs2 = require("fs");
47
+ var import_node_path = require("path");
41
48
  var import_asar = __toESM(require("@electron/asar"));
42
49
  var import_esbuild = require("esbuild");
43
50
 
@@ -58,26 +65,7 @@ var signature = (buffer, privateKey, cert, version) => {
58
65
  return encrypt(`${sig}%${version}`, key(cert, 32), key(buffer, 16));
59
66
  };
60
67
 
61
- // src/utils/zip.ts
62
- var import_node_fs = require("fs");
63
- var import_node_zlib = require("zlib");
64
- async function zipFile(filePath, targetFilePath = `${filePath}.gz`) {
65
- if (!(0, import_node_fs.existsSync)(filePath)) {
66
- throw new Error(`path to be zipped not exist: ${filePath}`);
67
- }
68
- const buffer = (0, import_node_fs.readFileSync)(filePath);
69
- return new Promise((resolve, reject) => {
70
- (0, import_node_zlib.gzip)(buffer, (err, buffer2) => {
71
- if (err) {
72
- reject(err);
73
- }
74
- (0, import_node_fs.writeFileSync)(targetFilePath, buffer2);
75
- resolve(null);
76
- });
77
- });
78
- }
79
-
80
- // src/utils/version.ts
68
+ // src/utils/noDep.ts
81
69
  function parseVersion(version) {
82
70
  const semver = /^(\d+)\.(\d+)\.(\d+)(?:-([a-zA-Z0-9\.-]+))?/i;
83
71
  const match = semver.exec(version);
@@ -102,11 +90,28 @@ function parseVersion(version) {
102
90
  }
103
91
  return ret;
104
92
  }
105
-
106
- // src/updateJson.ts
107
93
  function isUpdateJSON(json) {
108
- const is = (j) => "signature" in j && "version" in j && "size" in j && "minimumVersion" in j;
109
- return is(json) && "beta" in json && is(json.beta);
94
+ const is = (j) => !!(j && j.minimumVersion && j.signature && j.size && j.version);
95
+ return is(json) && is(json?.beta);
96
+ }
97
+
98
+ // src/utils/zip.ts
99
+ var import_node_fs = require("fs");
100
+ var import_node_zlib = require("zlib");
101
+ async function zipFile(filePath, targetFilePath = `${filePath}.gz`) {
102
+ if (!(0, import_node_fs.existsSync)(filePath)) {
103
+ throw new Error(`path to be zipped not exist: ${filePath}`);
104
+ }
105
+ const buffer = (0, import_node_fs.readFileSync)(filePath);
106
+ return new Promise((resolve2, reject) => {
107
+ (0, import_node_zlib.gzip)(buffer, (err, buffer2) => {
108
+ if (err) {
109
+ reject(err);
110
+ }
111
+ (0, import_node_fs.writeFileSync)(targetFilePath, buffer2);
112
+ resolve2(null);
113
+ });
114
+ });
110
115
  }
111
116
 
112
117
  // src/build-plugins/build.ts
@@ -117,8 +122,8 @@ async function buildAsar({
117
122
  electronDistPath,
118
123
  rendererDistPath
119
124
  }) {
120
- await (0, import_promises.rename)(rendererDistPath, `${electronDistPath}/renderer`);
121
- await (0, import_promises.writeFile)(`${electronDistPath}/version`, version);
125
+ await (0, import_promises.rename)(rendererDistPath, (0, import_node_path.join)(electronDistPath, "renderer"));
126
+ await (0, import_promises.writeFile)((0, import_node_path.join)(electronDistPath, "version"), version);
122
127
  await import_asar.default.createPackage(electronDistPath, asarOutputPath);
123
128
  await zipFile(asarOutputPath, gzipPath);
124
129
  }
@@ -146,13 +151,15 @@ async function buildVersion({
146
151
  };
147
152
  if ((0, import_node_fs2.existsSync)(versionPath)) {
148
153
  try {
149
- _json = JSON.parse(await (0, import_promises.readFile)(versionPath, "utf-8"));
154
+ const oldVersionJson = JSON.parse(await (0, import_promises.readFile)(versionPath, "utf-8"));
155
+ if (isUpdateJSON(oldVersionJson)) {
156
+ _json = oldVersionJson;
157
+ } else {
158
+ console.warn("old version json is invalid, ignore it");
159
+ }
150
160
  } catch (error) {
151
161
  }
152
162
  }
153
- if (!isUpdateJSON(_json)) {
154
- throw new Error("invalid version file");
155
- }
156
163
  const buffer = await (0, import_promises.readFile)(gzipPath);
157
164
  const sig = await (generateSignature ?? signature)(buffer, privateKey, cert, version);
158
165
  if (generateVersionJson) {
@@ -161,10 +168,12 @@ async function buildVersion({
161
168
  throw new Error("invalid version info");
162
169
  }
163
170
  } else {
164
- _json.beta.version = version;
165
- _json.beta.minimumVersion = minimumVersion;
166
- _json.beta.signature = sig;
167
- _json.beta.size = buffer.length;
171
+ _json.beta = {
172
+ version,
173
+ minimumVersion,
174
+ signature: sig,
175
+ size: buffer.length
176
+ };
168
177
  if (!parseVersion(version).stage) {
169
178
  _json.version = version;
170
179
  _json.minimumVersion = minimumVersion;
@@ -175,30 +184,57 @@ async function buildVersion({
175
184
  await (0, import_promises.writeFile)(versionPath, JSON.stringify(_json, null, 2));
176
185
  }
177
186
  async function buildEntry({
178
- entryPath,
179
- entryOutputPath: outfile,
180
- minify
187
+ sourcemap,
188
+ minify,
189
+ appEntryPath,
190
+ entryOutputDirPath,
191
+ nativeModuleEntryMap,
192
+ overrideEsbuildOptions,
193
+ postBuild
181
194
  }) {
182
195
  await (0, import_esbuild.build)({
183
- entryPoints: [entryPath],
196
+ entryPoints: {
197
+ entry: appEntryPath,
198
+ ...nativeModuleEntryMap
199
+ },
184
200
  bundle: true,
185
201
  platform: "node",
186
- outfile,
202
+ outdir: entryOutputDirPath,
187
203
  minify,
188
- external: ["electron", "original-fs"]
204
+ sourcemap,
205
+ entryNames: "[dir]/[name]",
206
+ assetNames: "[dir]/[name]",
207
+ external: ["electron", "original-fs"],
208
+ loader: {
209
+ ".node": "empty"
210
+ },
211
+ ...overrideEsbuildOptions
212
+ });
213
+ await postBuild?.({
214
+ getPathFromEntryOutputDir(...paths) {
215
+ return (0, import_node_path.join)(entryOutputDirPath, ...paths);
216
+ },
217
+ existsAndCopyToEntryOutputDir({ from, to, skipIfExist = true }) {
218
+ if ((0, import_node_fs2.existsSync)(from)) {
219
+ const target = (0, import_node_path.join)(entryOutputDirPath, to ?? (0, import_node_path.basename)(from));
220
+ if (!skipIfExist || !(0, import_node_fs2.existsSync)(target)) {
221
+ (0, import_node_fs2.cpSync)(from, target);
222
+ }
223
+ }
224
+ }
189
225
  });
190
226
  }
191
227
 
192
228
  // src/build-plugins/key.ts
193
229
  var import_node_fs3 = require("fs");
194
- var import_node_path = require("path");
230
+ var import_node_path2 = require("path");
195
231
  var import_selfsigned = require("selfsigned");
196
232
  function generateKeyPair(keyLength, subject, days, privateKeyPath, certPath) {
197
- const privateKeyDir = (0, import_node_path.dirname)(privateKeyPath);
233
+ const privateKeyDir = (0, import_node_path2.dirname)(privateKeyPath);
198
234
  if (!(0, import_node_fs3.existsSync)(privateKeyDir)) {
199
235
  (0, import_node_fs3.mkdirSync)(privateKeyDir, { recursive: true });
200
236
  }
201
- const certDir = (0, import_node_path.dirname)(certPath);
237
+ const certDir = (0, import_node_path2.dirname)(certPath);
202
238
  if (!(0, import_node_fs3.existsSync)(certDir)) {
203
239
  (0, import_node_fs3.mkdirSync)(certDir, { recursive: true });
204
240
  }
@@ -210,31 +246,21 @@ function generateKeyPair(keyLength, subject, days, privateKeyPath, certPath) {
210
246
  (0, import_node_fs3.writeFileSync)(privateKeyPath, privateKey.replace(/\r\n?/g, "\n"));
211
247
  (0, import_node_fs3.writeFileSync)(certPath, cert.replace(/\r\n?/g, "\n"));
212
248
  }
213
- function writeCertToMain(entryPath, cert) {
249
+ var noCertRegex = /(?<=const SIGNATURE_CERT\s*=\s*)['"]{2}/m;
250
+ var existCertRegex = /(?<=const SIGNATURE_CERT\s*=\s*)(['"]-----BEGIN CERTIFICATE-----[\s\S]*-----END CERTIFICATE-----\\n['"])/m;
251
+ function writeCertToEntry(entryPath, cert) {
252
+ if (!(0, import_node_fs3.existsSync)(entryPath)) {
253
+ throw new Error(`entry not exist: ${entryPath}`);
254
+ }
214
255
  const file = (0, import_node_fs3.readFileSync)(entryPath, "utf-8");
215
- const initRegex = /(?<=const SIGNATURE_CERT\s*=\s*)['"]{2}/m;
216
- const existRegex = /(?<=const SIGNATURE_CERT\s*=\s*)(['"]-----BEGIN CERTIFICATE-----[\s\S]*-----END CERTIFICATE-----\\n['"])/m;
217
- const eol = file.includes("\r") ? "\r\n" : "\n";
218
- const replacement = cert.split("\n").filter(Boolean).map((s) => `'${s}\\n'`).join(`${eol} + `);
256
+ const replacement = cert.split("\n").filter(Boolean).map((s) => `'${s}\\n'`).join("\n + ");
219
257
  let replaced = file;
220
- if (initRegex.test(file)) {
221
- replaced = file.replace(initRegex, replacement);
222
- } else if (existRegex.test(file)) {
223
- replaced = file.replace(existRegex, replacement);
258
+ if (noCertRegex.test(file)) {
259
+ replaced = file.replace(noCertRegex, replacement);
260
+ } else if (existCertRegex.test(file)) {
261
+ replaced = file.replace(existCertRegex, replacement);
224
262
  } else {
225
- const lines = file.split(eol);
226
- const r = `${eol}const SIGNATURE_CERT = ${replacement}${eol}`;
227
- let isMatched = false;
228
- for (let i = 0; i < lines.length; i++) {
229
- const line = lines[i];
230
- if (!line.startsWith("import") && !line.startsWith("/")) {
231
- lines.splice(i, 0, r);
232
- isMatched = true;
233
- break;
234
- }
235
- }
236
- !isMatched && lines.push(r);
237
- replaced = lines.join(eol);
263
+ throw new Error("no `SIGNATURE_CERT` found in entry");
238
264
  }
239
265
  (0, import_node_fs3.writeFileSync)(entryPath, replaced);
240
266
  }
@@ -242,45 +268,41 @@ function parseKeys({
242
268
  keyLength,
243
269
  privateKeyPath,
244
270
  certPath,
245
- entryPath,
271
+ appEntryPath,
246
272
  subject,
247
273
  days
248
274
  }) {
249
- const keysDir = (0, import_node_path.dirname)(privateKeyPath);
275
+ const keysDir = (0, import_node_path2.dirname)(privateKeyPath);
250
276
  !(0, import_node_fs3.existsSync)(keysDir) && (0, import_node_fs3.mkdirSync)(keysDir);
251
277
  if (!(0, import_node_fs3.existsSync)(privateKeyPath) || !(0, import_node_fs3.existsSync)(certPath)) {
278
+ console.warn("no key pair found, generate new key pair");
252
279
  generateKeyPair(keyLength, parseSubjects(subject), days, privateKeyPath, certPath);
253
280
  }
254
281
  const privateKey = process.env.UPDATER_PK || (0, import_node_fs3.readFileSync)(privateKeyPath, "utf-8");
255
282
  const cert = process.env.UPDATER_CERT || (0, import_node_fs3.readFileSync)(certPath, "utf-8");
256
- writeCertToMain(entryPath, cert);
257
- return {
258
- privateKey,
259
- cert
260
- };
283
+ writeCertToEntry(appEntryPath, cert);
284
+ return { privateKey, cert };
261
285
  }
262
286
  function parseSubjects(subject) {
263
- const ret = [];
264
- Object.keys(subject).forEach((name) => {
265
- const value = subject[name];
266
- value && ret.push({ name, value });
267
- });
268
- return ret;
287
+ return Object.entries(subject).filter(([_, value]) => !!value).map(([name, value]) => ({ name, value }));
269
288
  }
270
289
 
271
290
  // src/build-plugins/option.ts
272
- function parseOptions(options) {
291
+ function parseOptions(isBuild, pkg, options = {}) {
273
292
  const {
274
- isBuild,
275
- productName,
276
- version,
277
- minimumVersion = version,
278
- minify = false,
293
+ minimumVersion = "0.0.0",
294
+ entry: {
295
+ minify = isBuild,
296
+ sourcemap = isBuild,
297
+ entryOutputDirPath = "dist-entry",
298
+ appEntryPath = "electron/entry.ts",
299
+ nativeModuleEntryMap = {},
300
+ postBuild,
301
+ overrideEsbuildOptions
302
+ } = {},
279
303
  paths: {
280
- entryPath = "electron/app.ts",
281
- entryOutputPath = "app.js",
282
- asarOutputPath = `release/${productName}.asar`,
283
- gzipPath = `release/${productName}-${version}.asar.gz`,
304
+ asarOutputPath = `release/${pkg.name}.asar`,
305
+ gzipPath = `release/${pkg.name}-${pkg.version}.asar.gz`,
284
306
  electronDistPath = "dist-electron",
285
307
  rendererDistPath = "dist",
286
308
  versionPath = "version.json"
@@ -290,39 +312,43 @@ function parseOptions(options) {
290
312
  certPath = "keys/cert.pem",
291
313
  keyLength = 2048,
292
314
  certInfo = {},
293
- overrideFunctions = {}
315
+ overrideGenerator = {}
294
316
  } = {}
295
317
  } = options;
296
- const { generateSignature, generateVersionJson } = overrideFunctions;
318
+ const { generateSignature, generateVersionJson } = overrideGenerator;
297
319
  let {
298
320
  subject = {
299
- commonName: productName,
300
- organizationName: `org.${productName}`
321
+ commonName: pkg.name,
322
+ organizationName: `org.${pkg.name}`
301
323
  },
302
- days = 365
324
+ days = 3650
303
325
  } = certInfo;
304
326
  const buildAsarOption = {
305
- version,
327
+ version: pkg.version,
306
328
  asarOutputPath,
307
329
  gzipPath,
308
330
  electronDistPath,
309
331
  rendererDistPath
310
332
  };
311
333
  const buildEntryOption = {
312
- entryPath,
313
- entryOutputPath,
314
- minify
334
+ minify,
335
+ sourcemap,
336
+ entryOutputDirPath,
337
+ appEntryPath,
338
+ nativeModuleEntryMap,
339
+ postBuild,
340
+ overrideEsbuildOptions
315
341
  };
316
342
  const { privateKey, cert } = parseKeys({
317
343
  keyLength,
318
344
  privateKeyPath,
319
345
  certPath,
320
- entryPath,
346
+ appEntryPath,
321
347
  subject,
322
348
  days
323
349
  });
324
350
  const buildVersionOption = {
325
- version,
351
+ version: pkg.version,
326
352
  minimumVersion,
327
353
  gzipPath,
328
354
  privateKey,
@@ -331,34 +357,131 @@ function parseOptions(options) {
331
357
  generateSignature,
332
358
  generateVersionJson
333
359
  };
334
- return { isBuild, buildAsarOption, buildEntryOption, buildVersionOption };
360
+ return { buildAsarOption, buildEntryOption, buildVersionOption };
335
361
  }
336
362
 
337
363
  // src/vite.ts
338
- function ElectronUpdater(options) {
339
- const { isBuild, buildAsarOption, buildEntryOption, buildVersionOption } = parseOptions(options);
340
- const { entryPath, entryOutputPath } = buildEntryOption;
341
- const { asarOutputPath } = buildAsarOption;
342
- const id = "electron-incremental-updater";
343
- const log = (0, import_vite.createLogger)("info", { prefix: `[${id}]` });
344
- return {
345
- name: `vite-plugin-${id}`,
346
- enforce: "post",
347
- async closeBundle() {
348
- log.info("build entry start", { timestamp: true });
349
- await buildEntry(buildEntryOption);
350
- log.info(`build entry end, ${entryPath} -> ${entryOutputPath}`, { timestamp: true });
351
- if (!isBuild) {
352
- return;
353
- }
354
- log.info("build asar start", { timestamp: true });
355
- await buildAsar(buildAsarOption);
356
- await buildVersion(buildVersionOption);
357
- log.info(`build asar end, output to ${asarOutputPath}`, { timestamp: true });
364
+ function debugStartup(args) {
365
+ process.env.VSCODE_DEBUG ? console.log("[startup] Electron App") : args.startup();
366
+ }
367
+ var id = "electron-incremental-updater";
368
+ var log = (0, import_vite.createLogger)("info", { prefix: `[${id}]` });
369
+ function electronWithUpdater(options) {
370
+ const {
371
+ isBuild,
372
+ pkg,
373
+ main: _main,
374
+ preload: _preload,
375
+ updater,
376
+ useNotBundle = true,
377
+ logParsedOptions
378
+ } = options;
379
+ const _options = parseOptions(isBuild, pkg, updater);
380
+ try {
381
+ (0, import_node_fs4.rmSync)(_options.buildAsarOption.electronDistPath, { recursive: true, force: true });
382
+ (0, import_node_fs4.rmSync)(_options.buildEntryOption.entryOutputDirPath, { recursive: true, force: true });
383
+ } catch (ignore) {
384
+ }
385
+ log.info(`remove old files`, { timestamp: true });
386
+ const { buildAsarOption, buildEntryOption, buildVersionOption } = _options;
387
+ const { entryOutputDirPath, nativeModuleEntryMap, appEntryPath } = buildEntryOption;
388
+ const sourcemap = isBuild || !!process.env.VSCODE_DEBUG;
389
+ const _appPath = (0, import_node_path3.join)(entryOutputDirPath, "entry.js");
390
+ if ((0, import_node_path3.resolve)((0, import_vite.normalizePath)(pkg.main)) !== (0, import_node_path3.resolve)((0, import_vite.normalizePath)(_appPath))) {
391
+ throw new Error(`wrong "main" field in package.json: "${pkg.main}", it should be "${(0, import_vite.normalizePath)(_appPath)}"`);
392
+ }
393
+ let isInit = false;
394
+ const _buildEntry = async () => {
395
+ await buildEntry(buildEntryOption);
396
+ log.info(`build entry to '${entryOutputDirPath}'`, { timestamp: true });
397
+ };
398
+ const electronPluginOptions = {
399
+ main: {
400
+ entry: _main.files,
401
+ onstart: async (args) => {
402
+ if (!isInit) {
403
+ isInit = true;
404
+ await _buildEntry();
405
+ }
406
+ _main.onstart?.(args) ?? args.startup();
407
+ },
408
+ vite: (0, import_vite.mergeConfig)(
409
+ {
410
+ plugins: !isBuild && useNotBundle ? [(0, import_plugin.notBundle)()] : void 0,
411
+ build: {
412
+ sourcemap,
413
+ minify: isBuild,
414
+ outDir: `${buildAsarOption.electronDistPath}/main`,
415
+ rollupOptions: {
416
+ external: Object.keys("dependencies" in pkg ? pkg.dependencies : {})
417
+ }
418
+ }
419
+ },
420
+ _main.vite ?? {}
421
+ )
422
+ },
423
+ preload: {
424
+ input: _preload.files,
425
+ onstart: _preload.onstart,
426
+ vite: (0, import_vite.mergeConfig)(
427
+ {
428
+ plugins: [
429
+ {
430
+ name: `${id}-build`,
431
+ enforce: "post",
432
+ apply() {
433
+ return isBuild;
434
+ },
435
+ async closeBundle() {
436
+ await _buildEntry();
437
+ await buildAsar(buildAsarOption);
438
+ log.info(`build asar to '${buildAsarOption.asarOutputPath}'`, { timestamp: true });
439
+ await buildVersion(buildVersionOption);
440
+ log.info(`build version info to '${buildVersionOption.versionPath}'`, { timestamp: true });
441
+ }
442
+ }
443
+ ],
444
+ build: {
445
+ sourcemap: sourcemap ? "inline" : void 0,
446
+ minify: isBuild,
447
+ outDir: `${buildAsarOption.electronDistPath}/preload`,
448
+ rollupOptions: {
449
+ external: Object.keys("dependencies" in pkg ? pkg.dependencies : {})
450
+ }
451
+ }
452
+ },
453
+ _preload.vite ?? {}
454
+ )
358
455
  }
359
456
  };
457
+ logParsedOptions && log.info(
458
+ JSON.stringify(
459
+ {
460
+ ...electronPluginOptions,
461
+ updater: { buildAsarOption, buildEntryOption, buildVersionOption }
462
+ },
463
+ (key2, value) => key2 === "privateKey" || key2 === "cert" ? "***" : value,
464
+ 2
465
+ ),
466
+ { timestamp: true }
467
+ );
468
+ let extraHmrPlugin;
469
+ if (nativeModuleEntryMap) {
470
+ const files = [...Object.values(nativeModuleEntryMap), appEntryPath].map((file) => (0, import_node_path3.resolve)((0, import_vite.normalizePath)(file)));
471
+ extraHmrPlugin = {
472
+ name: `${id}-dev`,
473
+ apply() {
474
+ return !isBuild;
475
+ },
476
+ configureServer: (server) => {
477
+ server.watcher.add(files).on("change", (p) => files.includes(p) && _buildEntry().then(() => (0, import_vite_plugin_electron.startup)()));
478
+ }
479
+ };
480
+ }
481
+ return [(0, import_simple.default)(electronPluginOptions), extraHmrPlugin];
360
482
  }
361
483
  // Annotate the CommonJS export names for ESM import in node:
362
484
  0 && (module.exports = {
363
- ElectronUpdater
485
+ debugStartup,
486
+ electronWithUpdater
364
487
  });