electron-incremental-update 0.9.0 → 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,29 +184,60 @@ 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);
198
- (0, import_node_fs3.existsSync)(privateKeyDir) || (0, import_node_fs3.mkdirSync)(privateKeyDir, { recursive: true });
199
- const certDir = (0, import_node_path.dirname)(certPath);
200
- (0, import_node_fs3.existsSync)(certDir) || (0, import_node_fs3.mkdirSync)(certDir, { recursive: true });
233
+ const privateKeyDir = (0, import_node_path2.dirname)(privateKeyPath);
234
+ if (!(0, import_node_fs3.existsSync)(privateKeyDir)) {
235
+ (0, import_node_fs3.mkdirSync)(privateKeyDir, { recursive: true });
236
+ }
237
+ const certDir = (0, import_node_path2.dirname)(certPath);
238
+ if (!(0, import_node_fs3.existsSync)(certDir)) {
239
+ (0, import_node_fs3.mkdirSync)(certDir, { recursive: true });
240
+ }
201
241
  const { cert, private: privateKey } = (0, import_selfsigned.generate)(subject, {
202
242
  keySize: keyLength,
203
243
  algorithm: "sha256",
@@ -206,31 +246,21 @@ function generateKeyPair(keyLength, subject, days, privateKeyPath, certPath) {
206
246
  (0, import_node_fs3.writeFileSync)(privateKeyPath, privateKey.replace(/\r\n?/g, "\n"));
207
247
  (0, import_node_fs3.writeFileSync)(certPath, cert.replace(/\r\n?/g, "\n"));
208
248
  }
209
- 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
+ }
210
255
  const file = (0, import_node_fs3.readFileSync)(entryPath, "utf-8");
211
- const initRegex = /(?<=const SIGNATURE_CERT\s*=\s*)['"]{2}/m;
212
- const existRegex = /(?<=const SIGNATURE_CERT\s*=\s*)(['"]-----BEGIN CERTIFICATE-----[\s\S]*-----END CERTIFICATE-----\\n['"])/m;
213
- const eol = file.includes("\r") ? "\r\n" : "\n";
214
- 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 + ");
215
257
  let replaced = file;
216
- if (initRegex.test(file)) {
217
- replaced = file.replace(initRegex, replacement);
218
- } else if (existRegex.test(file)) {
219
- 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);
220
262
  } else {
221
- const lines = file.split(eol);
222
- const r = `${eol}const SIGNATURE_CERT = ${replacement}${eol}`;
223
- let isMatched = false;
224
- for (let i = 0; i < lines.length; i++) {
225
- const line = lines[i];
226
- if (!line.startsWith("import") && !line.startsWith("/")) {
227
- lines.splice(i, 0, r);
228
- isMatched = true;
229
- break;
230
- }
231
- }
232
- !isMatched && lines.push(r);
233
- replaced = lines.join(eol);
263
+ throw new Error("no `SIGNATURE_CERT` found in entry");
234
264
  }
235
265
  (0, import_node_fs3.writeFileSync)(entryPath, replaced);
236
266
  }
@@ -238,45 +268,40 @@ function parseKeys({
238
268
  keyLength,
239
269
  privateKeyPath,
240
270
  certPath,
241
- entryPath,
271
+ appEntryPath,
242
272
  subject,
243
273
  days
244
274
  }) {
245
- const keysDir = (0, import_node_path.dirname)(privateKeyPath);
275
+ const keysDir = (0, import_node_path2.dirname)(privateKeyPath);
246
276
  !(0, import_node_fs3.existsSync)(keysDir) && (0, import_node_fs3.mkdirSync)(keysDir);
247
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");
248
279
  generateKeyPair(keyLength, parseSubjects(subject), days, privateKeyPath, certPath);
249
280
  }
250
281
  const privateKey = process.env.UPDATER_PK || (0, import_node_fs3.readFileSync)(privateKeyPath, "utf-8");
251
282
  const cert = process.env.UPDATER_CERT || (0, import_node_fs3.readFileSync)(certPath, "utf-8");
252
- writeCertToMain(entryPath, cert);
253
- return {
254
- privateKey,
255
- cert
256
- };
283
+ writeCertToEntry(appEntryPath, cert);
284
+ return { privateKey, cert };
257
285
  }
258
286
  function parseSubjects(subject) {
259
- const ret = [];
260
- Object.keys(subject).forEach((name) => {
261
- const value = subject[name];
262
- value && ret.push({ name, value });
263
- });
264
- return ret;
287
+ return Object.entries(subject).filter(([_, value]) => !!value).map(([name, value]) => ({ name, value }));
265
288
  }
266
289
 
267
290
  // src/build-plugins/option.ts
268
- function parseOptions(options) {
291
+ function parseOptions(isBuild, pkg, options = {}) {
269
292
  const {
270
- isBuild,
271
- productName,
272
- version,
273
- minimumVersion = version,
274
- 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
+ } = {},
275
302
  paths: {
276
- entryPath = "electron/app.ts",
277
- entryOutputPath = "app.js",
278
- asarOutputPath = `release/${productName}.asar`,
279
- gzipPath = `release/${productName}-${version}.asar.gz`,
303
+ asarOutputPath = `release/${pkg.name}.asar`,
304
+ gzipPath = `release/${pkg.name}-${pkg.version}.asar.gz`,
280
305
  electronDistPath = "dist-electron",
281
306
  rendererDistPath = "dist",
282
307
  versionPath = "version.json"
@@ -286,39 +311,42 @@ function parseOptions(options) {
286
311
  certPath = "keys/cert.pem",
287
312
  keyLength = 2048,
288
313
  certInfo = {},
289
- overrideFunctions = {}
314
+ overrideGenerator = {}
290
315
  } = {}
291
316
  } = options;
292
- const { generateSignature, generateVersionJson } = overrideFunctions;
317
+ const { generateSignature, generateVersionJson } = overrideGenerator;
293
318
  let {
294
319
  subject = {
295
- commonName: productName,
296
- organizationName: `org.${productName}`
320
+ commonName: pkg.name,
321
+ organizationName: `org.${pkg.name}`
297
322
  },
298
- days = 365
323
+ days = 3650
299
324
  } = certInfo;
300
325
  const buildAsarOption = {
301
- version,
326
+ version: pkg.version,
302
327
  asarOutputPath,
303
328
  gzipPath,
304
329
  electronDistPath,
305
330
  rendererDistPath
306
331
  };
307
332
  const buildEntryOption = {
308
- entryPath,
309
- entryOutputPath,
310
- minify
333
+ minify,
334
+ sourcemap,
335
+ entryOutputDirPath,
336
+ appEntryPath,
337
+ nativeModuleEntryMap,
338
+ postBuild
311
339
  };
312
340
  const { privateKey, cert } = parseKeys({
313
341
  keyLength,
314
342
  privateKeyPath,
315
343
  certPath,
316
- entryPath,
344
+ appEntryPath,
317
345
  subject,
318
346
  days
319
347
  });
320
348
  const buildVersionOption = {
321
- version,
349
+ version: pkg.version,
322
350
  minimumVersion,
323
351
  gzipPath,
324
352
  privateKey,
@@ -327,34 +355,131 @@ function parseOptions(options) {
327
355
  generateSignature,
328
356
  generateVersionJson
329
357
  };
330
- return { isBuild, buildAsarOption, buildEntryOption, buildVersionOption };
358
+ return { buildAsarOption, buildEntryOption, buildVersionOption };
331
359
  }
332
360
 
333
361
  // src/vite.ts
334
- function ElectronUpdater(options) {
335
- const { isBuild, buildAsarOption, buildEntryOption, buildVersionOption } = parseOptions(options);
336
- const { entryPath, entryOutputPath } = buildEntryOption;
337
- const { asarOutputPath } = buildAsarOption;
338
- const id = "electron-incremental-updater";
339
- const log = (0, import_vite.createLogger)("info", { prefix: `[${id}]` });
340
- return {
341
- name: `vite-plugin-${id}`,
342
- enforce: "post",
343
- async closeBundle() {
344
- log.info("build entry start", { timestamp: true });
345
- await buildEntry(buildEntryOption);
346
- log.info(`build entry end, ${entryPath} -> ${entryOutputPath}`, { timestamp: true });
347
- if (!isBuild) {
348
- return;
349
- }
350
- log.info("build asar start", { timestamp: true });
351
- await buildAsar(buildAsarOption);
352
- await buildVersion(buildVersionOption);
353
- 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
+ )
354
453
  }
355
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];
356
480
  }
357
481
  // Annotate the CommonJS export names for ESM import in node:
358
482
  0 && (module.exports = {
359
- ElectronUpdater
483
+ debugStartup,
484
+ electronWithUpdater
360
485
  });