electron-incremental-update 0.9.1 → 1.0.0

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,40 @@ 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
+ } = {},
279
302
  paths: {
280
- entryPath = "electron/app.ts",
281
- entryOutputPath = "app.js",
282
- asarOutputPath = `release/${productName}.asar`,
283
- gzipPath = `release/${productName}-${version}.asar.gz`,
303
+ asarOutputPath = `release/${pkg.name}.asar`,
304
+ gzipPath = `release/${pkg.name}-${pkg.version}.asar.gz`,
284
305
  electronDistPath = "dist-electron",
285
306
  rendererDistPath = "dist",
286
307
  versionPath = "version.json"
@@ -290,39 +311,42 @@ function parseOptions(options) {
290
311
  certPath = "keys/cert.pem",
291
312
  keyLength = 2048,
292
313
  certInfo = {},
293
- overrideFunctions = {}
314
+ overrideGenerator = {}
294
315
  } = {}
295
316
  } = options;
296
- const { generateSignature, generateVersionJson } = overrideFunctions;
317
+ const { generateSignature, generateVersionJson } = overrideGenerator;
297
318
  let {
298
319
  subject = {
299
- commonName: productName,
300
- organizationName: `org.${productName}`
320
+ commonName: pkg.name,
321
+ organizationName: `org.${pkg.name}`
301
322
  },
302
- days = 365
323
+ days = 3650
303
324
  } = certInfo;
304
325
  const buildAsarOption = {
305
- version,
326
+ version: pkg.version,
306
327
  asarOutputPath,
307
328
  gzipPath,
308
329
  electronDistPath,
309
330
  rendererDistPath
310
331
  };
311
332
  const buildEntryOption = {
312
- entryPath,
313
- entryOutputPath,
314
- minify
333
+ minify,
334
+ sourcemap,
335
+ entryOutputDirPath,
336
+ appEntryPath,
337
+ nativeModuleEntryMap,
338
+ postBuild
315
339
  };
316
340
  const { privateKey, cert } = parseKeys({
317
341
  keyLength,
318
342
  privateKeyPath,
319
343
  certPath,
320
- entryPath,
344
+ appEntryPath,
321
345
  subject,
322
346
  days
323
347
  });
324
348
  const buildVersionOption = {
325
- version,
349
+ version: pkg.version,
326
350
  minimumVersion,
327
351
  gzipPath,
328
352
  privateKey,
@@ -331,34 +355,131 @@ function parseOptions(options) {
331
355
  generateSignature,
332
356
  generateVersionJson
333
357
  };
334
- return { isBuild, buildAsarOption, buildEntryOption, buildVersionOption };
358
+ return { buildAsarOption, buildEntryOption, buildVersionOption };
335
359
  }
336
360
 
337
361
  // 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 });
362
+ function debugStartup(args) {
363
+ process.env.VSCODE_DEBUG ? console.log("[startup] Electron App") : args.startup();
364
+ }
365
+ var id = "electron-incremental-updater";
366
+ var log = (0, import_vite.createLogger)("info", { prefix: `[${id}]` });
367
+ function electronWithUpdater(options) {
368
+ const {
369
+ isBuild,
370
+ pkg,
371
+ main: _main,
372
+ preload: _preload,
373
+ updater,
374
+ useNotBundle = true,
375
+ logParsedOptions
376
+ } = options;
377
+ const _options = parseOptions(isBuild, pkg, updater);
378
+ try {
379
+ (0, import_node_fs4.rmSync)(_options.buildAsarOption.electronDistPath, { recursive: true, force: true });
380
+ (0, import_node_fs4.rmSync)(_options.buildEntryOption.entryOutputDirPath, { recursive: true, force: true });
381
+ } catch (ignore) {
382
+ }
383
+ log.info(`remove old files`, { timestamp: true });
384
+ const { buildAsarOption, buildEntryOption, buildVersionOption } = _options;
385
+ const { entryOutputDirPath, nativeModuleEntryMap, appEntryPath } = buildEntryOption;
386
+ const sourcemap = isBuild || !!process.env.VSCODE_DEBUG;
387
+ const _appPath = (0, import_node_path3.join)(entryOutputDirPath, "entry.js");
388
+ if ((0, import_node_path3.resolve)((0, import_vite.normalizePath)(pkg.main)) !== (0, import_node_path3.resolve)((0, import_vite.normalizePath)(_appPath))) {
389
+ throw new Error(`wrong "main" field in package.json: "${pkg.main}", it should be "${_appPath.replace(/\\/g, "/")}"`);
390
+ }
391
+ let isInit = false;
392
+ const _buildEntry = async () => {
393
+ await buildEntry(buildEntryOption);
394
+ log.info(`build entry to '${entryOutputDirPath}'`, { timestamp: true });
395
+ };
396
+ const electronPluginOptions = {
397
+ main: {
398
+ entry: _main.files,
399
+ onstart: async (args) => {
400
+ if (!isInit) {
401
+ isInit = true;
402
+ await _buildEntry();
403
+ }
404
+ _main.onstart?.(args) ?? args.startup();
405
+ },
406
+ vite: (0, import_vite.mergeConfig)(
407
+ {
408
+ plugins: !isBuild && useNotBundle ? [(0, import_plugin.notBundle)()] : void 0,
409
+ build: {
410
+ sourcemap,
411
+ minify: isBuild,
412
+ outDir: `${buildAsarOption.electronDistPath}/main`,
413
+ rollupOptions: {
414
+ external: Object.keys("dependencies" in pkg ? pkg.dependencies : {})
415
+ }
416
+ }
417
+ },
418
+ _main.vite ?? {}
419
+ )
420
+ },
421
+ preload: {
422
+ input: _preload.files,
423
+ onstart: _preload.onstart,
424
+ vite: (0, import_vite.mergeConfig)(
425
+ {
426
+ plugins: [
427
+ {
428
+ name: `${id}-build`,
429
+ enforce: "post",
430
+ apply() {
431
+ return isBuild;
432
+ },
433
+ async closeBundle() {
434
+ await _buildEntry();
435
+ await buildAsar(buildAsarOption);
436
+ log.info(`build asar to '${buildAsarOption.asarOutputPath}'`, { timestamp: true });
437
+ await buildVersion(buildVersionOption);
438
+ log.info(`build version info to '${buildVersionOption.versionPath}'`, { timestamp: true });
439
+ }
440
+ }
441
+ ],
442
+ build: {
443
+ sourcemap: sourcemap ? "inline" : void 0,
444
+ minify: isBuild,
445
+ outDir: `${buildAsarOption.electronDistPath}/preload`,
446
+ rollupOptions: {
447
+ external: Object.keys("dependencies" in pkg ? pkg.dependencies : {})
448
+ }
449
+ }
450
+ },
451
+ _preload.vite ?? {}
452
+ )
358
453
  }
359
454
  };
455
+ logParsedOptions && log.info(
456
+ JSON.stringify(
457
+ {
458
+ ...electronPluginOptions,
459
+ updater: { buildAsarOption, buildEntryOption, buildVersionOption }
460
+ },
461
+ (key2, value) => key2 === "privateKey" || key2 === "cert" ? "***" : value,
462
+ 2
463
+ ),
464
+ { timestamp: true }
465
+ );
466
+ let extraHmrPlugin;
467
+ if (nativeModuleEntryMap) {
468
+ const files = [...Object.values(nativeModuleEntryMap), appEntryPath].map((file) => (0, import_node_path3.resolve)((0, import_vite.normalizePath)(file)));
469
+ extraHmrPlugin = {
470
+ name: `${id}-dev`,
471
+ apply() {
472
+ return !isBuild;
473
+ },
474
+ configureServer: (server) => {
475
+ server.watcher.add(files).on("change", (p) => files.includes(p) && _buildEntry().then(() => (0, import_vite_plugin_electron.startup)()));
476
+ }
477
+ };
478
+ }
479
+ return [(0, import_simple.default)(electronPluginOptions), extraHmrPlugin];
360
480
  }
361
481
  // Annotate the CommonJS export names for ESM import in node:
362
482
  0 && (module.exports = {
363
- ElectronUpdater
483
+ debugStartup,
484
+ electronWithUpdater
364
485
  });