electron-incremental-update 2.0.0-beta.9 → 2.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/README.md CHANGED
@@ -1,41 +1,45 @@
1
- ## Electron Incremental Updater
1
+ ## Electron Incremental Update
2
2
 
3
- This project is based on [vite-plugin-electron](https://github.com/electron-vite/vite-plugin-electron), provide a plugin that build on top of `ElectronSimple`, an `Updater` class and some useful utils for Electron.
3
+ This project is built on top of [vite-plugin-electron](https://github.com/electron-vite/vite-plugin-electron), offers a lightweight update solution for Electron applications without using native executables.
4
4
 
5
- There will be two asar in production, `app.asar` and `${electron.app.name}.asar` (also as the `name` field in `package.json`).
5
+ ### Key Features
6
6
 
7
- The `app.asar` is used to load `${electron.app.name}.asar` and initialize the `Updater`.
7
+ The solution includes a Vite plugin, a startup entry function, an `Updater` class, and a set of utilities for Electron.
8
8
 
9
- The new `${electron.app.name}.asar`, which can download from remote or load from buffer, will be verified by `Updater` using presigned RSA + Signature. While passing the check and restart, the old `${electron.app.name}.asar` will be replaced by the new one. Hooks like `beforeDoUpdate` are provided.
9
+ It use 2 asar file structure for updates:
10
10
 
11
- All **native modules** should be packaged into `app.asar` to reduce `${electron.app.name}.asar` file size, [see usage](#use-native-modules). Therefore, auto upgrade of portable app is possible.
11
+ - `app.asar`: The application entry, loads the `${electron.app.name}.asar` and initializes the updater on startup
12
+ - `${electron.app.name}.asar`: The package that contains main / preload / renderer process code
12
13
 
13
- Support bytecode protection, [see details](#bytecode-protection)
14
+ ### Update Steps
14
15
 
15
- No `vite-plugin-electron-renderer` config
16
+ 1. Check update from remote server
17
+ 2. If update available, download the update asar, verify by presigned RSA + Signature and write to disk
18
+ 3. Quit and restart the app
19
+ 4. Replace the old `${electron.app.name}.asar` on startup and load the new one
16
20
 
17
- - inspired by [Obsidian](https://obsidian.md/)'s upgrade strategy
21
+ ### Other Features
18
22
 
19
- ## Install
23
+ - Update size reduction: All **native modules** should be packaged into `app.asar` to reduce `${electron.app.name}.asar` file size, [see usage](#use-native-modules)
24
+ - Bytecode protection: Use V8 cache to protect source code, [see details](#bytecode-protection)
20
25
 
21
- ### npm
22
- ```bash
23
- npm install -D vite-plugin-electron electron-incremental-update
26
+ ## Getting Started
27
+
28
+ ### Install
29
+
30
+ ```sh
31
+ npm install -D electron-incremental-update
24
32
  ```
25
- ### yarn
26
- ```bash
27
- yarn add -D vite-plugin-electron electron-incremental-update
33
+ ```sh
34
+ yarn add -D electron-incremental-update
28
35
  ```
29
- ### pnpm
30
- ```bash
31
- pnpm add -D vite-plugin-electron electron-incremental-update
36
+ ```sh
37
+ pnpm add -D electron-incremental-update
32
38
  ```
33
39
 
34
- ## Getting started
40
+ ### Project Structure
35
41
 
36
- ### Project structure
37
-
38
- base on [electron-vite-vue](https://github.com/electron-vite/electron-vite-vue)
42
+ Base on [electron-vite-vue](https://github.com/electron-vite/electron-vite-vue)
39
43
 
40
44
  ```
41
45
  electron
@@ -50,37 +54,43 @@ src
50
54
  └── ...
51
55
  ```
52
56
 
53
- ### Setup entry
57
+ ### Setup Entry
58
+
59
+ The entry is used to load the application and initialize the `Updater`
54
60
 
55
- in `electron/entry.ts` (build by `Esbuild`)
61
+ `Updater` use the `provider` to check and download the update. The built-in `GithubProvider` is based on `BaseProvider`, which implements the `IProvider` interface (see [types](#provider)). And the `provider` is optional, you can setup later
62
+
63
+ in `electron/entry.ts`
56
64
 
57
65
  ```ts
58
- import { initApp } from 'electron-incremental-update'
59
- import { parseGithubCdnURL } from 'electron-incremental-update/utils'
60
- import { repository } from '../package.json'
66
+ import { createElectronApp } from 'electron-incremental-update'
67
+ import { GitHubProvider } from 'electron-incremental-update/provider'
61
68
 
62
- initApp({
63
- // can be updater option or function that return updater
69
+ createElectronApp({
64
70
  updater: {
65
- SIGNATURE_CERT: 'custom certificate',
66
- repository,
67
- updateJsonURL: parseGithubCdnURL(repository, jsonPrefix, 'version.json'),
68
- releaseAsarURL: parseGithubCdnURL(repository, asarPrefix, `download/latest/${app.name}.asar.gz`),
69
- receiveBeta: true,
71
+ // optinal, you can setup later
72
+ provider: new GitHubProvider({
73
+ username: 'yourname',
74
+ repo: 'electron',
75
+ }),
76
+ },
77
+ beforeStart(mainFilePath, logger) {
78
+ logger?.debug(mainFilePath)
70
79
  },
71
- onStart: console.log
72
80
  })
73
81
  ```
74
82
 
75
- - [some CDN resources](https://github.com/XIU2/UserScript/blob/master/GithubEnhanced-High-Speed-Download.user.js#L34):
83
+ - [some Github CDN resources](https://github.com/XIU2/UserScript/blob/master/GithubEnhanced-High-Speed-Download.user.js#L34)
76
84
 
77
85
  ### Setup `vite.config.ts`
78
86
 
79
- All options are documented with JSDoc
87
+ The plugin config, `main` and `preload` parts are reference from [electron-vite-vue](https://github.com/electron-vite/electron-vite-vue)
80
88
 
81
89
  - certificate will read from `process.env.UPDATER_CERT` first, if absend, read config
82
90
  - privatekey will read from `process.env.UPDATER_PK` first, if absend, read config
83
91
 
92
+ See all config in [types](#plugin)
93
+
84
94
  in `vite.config.mts`
85
95
 
86
96
  ```ts
@@ -126,7 +136,7 @@ export default defineConfig(async ({ command }) => {
126
136
  }
127
137
  ```
128
138
 
129
- ### Config electron-builder
139
+ ### Config `electron-builder`
130
140
 
131
141
  ```js
132
142
  const { name } = require('./package.json')
@@ -159,24 +169,13 @@ module.exports = {
159
169
 
160
170
  ## Usage
161
171
 
162
- ### Use in main process
172
+ ### Use In Main Process
163
173
 
164
- To use electron's `net` module for updating, the `checkUpdate` and `download` functions must be called after the app is ready by default. You have the option to customize the download function when creating the updater.
174
+ In most cases, you should also setup the `UpdateProvider` before updating, unless you setup params when calling `checkUpdate` or `downloadUpdate`.
165
175
 
166
- **NOTE: There should only one function and should be default export in the entry file**
176
+ The update steps are similar to [electron-updater](https://github.com/electron-userland/electron-updater) and have same methods and events on `Updater`
167
177
 
168
- in `electron/entry.ts`
169
-
170
- ```ts
171
- initApp({
172
- updater: {
173
- overrideFunctions: {
174
- downloadJSON: (url: string, headers: Record<string, any>) => {}
175
- // ...
176
- }
177
- },
178
- })
179
- ```
178
+ **NOTE: There should only one function and should be default export in the main index file**
180
179
 
181
180
  in `electron/main/index.ts`
182
181
 
@@ -188,45 +187,56 @@ import { app } from 'electron'
188
187
  export default startupWithUpdater((updater) => {
189
188
  await app.whenReady()
190
189
 
191
- const { appVersion, electronVersion, entryVersion } = getVersions()
192
- console.log(`${app.name}.asar path`, getPathFromAppNameAsar())
193
- console.log('app version:', appVersion)
194
- console.log('entry (installer) version', entryVersion)
195
- console.log('electron version', electronVersion)
190
+ console.table({
191
+ [`${app.name}.asar path:`]: getPathFromAppNameAsar(),
192
+ 'app version:': getAppVersion(),
193
+ 'entry (installer) version:': getEntryVersion(),
194
+ 'electron version:': process.versions.electron,
195
+ })
196
196
 
197
197
  updater.onDownloading = ({ percent }) => {
198
198
  console.log(percent)
199
199
  }
200
200
  updater.logger = console
201
201
  updater.receiveBeta = true
202
+ // setup provider later
203
+ updater.provider = new GitHubProvider({
204
+ user: 'yourname',
205
+ repo: 'electron',
206
+ // setup url handler
207
+ urlHandler: (url) => {
208
+ url.hostname = 'mirror.ghproxy.com'
209
+ url.pathname = `https://github.com${url.pathname}`
210
+ return url
211
+ }
212
+ })
202
213
 
203
- updater.checkUpdate().then(async (result) => {
204
- if (result === undefined) {
205
- console.log('Update Unavailable')
206
- } else if (result instanceof UpdaterError) {
207
- console.error(result)
208
- } else {
209
- console.log('new version: ', result.version)
210
- const { response } = await dialog.showMessageBox({
211
- type: 'info',
212
- buttons: ['Download', 'Later'],
213
- message: 'Application update available!',
214
- })
215
- if (response !== 0) {
216
- return
217
- }
218
- const downloadResult = await updater.download()
219
- if (downloadResult) {
220
- updater.quitAndInstall()
221
- }
214
+ updater.on('update-available', async ({ version }) => {
215
+ const { response } = await dialog.showMessageBox({
216
+ type: 'info',
217
+ buttons: ['Download', 'Later'],
218
+ message: `v${version} update available!`,
219
+ })
220
+ if (response !== 0) {
221
+ return
222
222
  }
223
+ await updater.downloadUpdate()
224
+ })
225
+ updater.on('update-not-available', (reason, info) => console.log(reason, info))
226
+ updater.on('download-progress', (data) => {
227
+ console.log(data)
228
+ main.send(BrowserWindow.getAllWindows()[0], 'msg', data)
223
229
  })
230
+ updater.on('update-downloaded', () => {
231
+ updater.quitAndInstall()
232
+ })
233
+ updater.checkForUpdates()
224
234
  })
225
235
  ```
226
236
 
227
- ### Use native modules
237
+ ### Use Native Modules
228
238
 
229
- All the **native modules** should be set as `dependency` in `package.json`. `electron-rebuild` only check dependencies inside `dependency` field.
239
+ To reduce production size, it is recommended that all the **native modules** should be set as `dependency` in `package.json` and other packages should be set as `devDependencies`. Also, `electron-rebuild` only check dependencies inside `dependency` field.
230
240
 
231
241
  If you are using `electron-builder` to build distributions, all the native modules with its **large relavent `node_modiles`** will be packaged into `app.asar` by default.
232
242
 
@@ -235,7 +245,7 @@ Luckily, `Esbuild` can bundle all the dependencies. Just follow the steps:
235
245
  1. setup `nativeModuleEntryMap` option
236
246
  2. Manually copy the native binaries in `postBuild` callback
237
247
  3. Exclude all the dependencies in `electron-builder`'s config
238
- 4. call the native functions with `loadNativeModuleFromEntry` in your code
248
+ 4. call the native functions with `requireNative` in your code
239
249
 
240
250
  #### Example
241
251
 
@@ -298,9 +308,7 @@ export function test(): void {
298
308
  in `electron/main/service.ts`
299
309
 
300
310
  ```ts
301
- import { loadNativeModuleFromEntry } from 'electron-incremental-update/utils'
302
-
303
- const requireNative = loadNativeModuleFromEntry()
311
+ import { requireNative } from 'electron-incremental-update/utils'
304
312
 
305
313
  requireNative<typeof import('../native/db')>('db').test()
306
314
  ```
@@ -311,131 +319,202 @@ in `electron-builder.config.js`
311
319
  module.exports = {
312
320
  files: [
313
321
  'dist-entry',
314
- // exclude dependencies in electron-builder config
322
+ // exclude all dependencies in electron-builder config
315
323
  '!node_modules/**',
316
324
  ]
317
325
  }
318
326
  ```
319
327
 
320
- ### Bytecode protection
321
-
322
- From v1.2, the vite plugin is able to generate bytecode to protect your application.
328
+ ### Bytecode Protection
323
329
 
324
- It will automatically protect your `SIGNATURE_CERT` by default.
325
-
326
- credit to [electron-vite](https://github.com/alex8088/electron-vite/blob/master/src/plugins/bytecode.ts), and improve the string protection (see [original issue](https://github.com/alex8088/electron-vite/issues/552))
330
+ Use V8 cache to protect the source code
327
331
 
328
332
  ```ts
329
333
  electronWithUpdater({
330
334
  // ...
331
- bytecode: true,
335
+ bytecode: true, // or options
332
336
  })
333
337
  ```
334
338
 
339
+ #### Benifits
340
+
341
+ https://electron-vite.org/guide/source-code-protection
342
+
343
+ - Improve the string protection (see [original issue](https://github.com/alex8088/electron-vite/issues/552))
344
+ - Protect all strings by default
345
+ - Minification is allowed
346
+
335
347
  #### Limitation
336
348
 
337
- - only support commonjs
338
- - only for main process by default, if you want to use in preload script, please use `electronWithUpdater({ bytecode: { enablePreload: true } })` and set `sandbox: false` when creating window
349
+ - Only support commonjs
350
+ - Only for main process by default, if you want to use in preload script, please use `electronWithUpdater({ bytecode: { enablePreload: true } })` and set `sandbox: false` when creating window
339
351
 
340
352
  ### Types
341
353
 
342
- #### Updater
354
+ #### Entry
343
355
 
344
356
  ```ts
345
- export interface UpdaterOption {
357
+ export interface AppOption {
346
358
  /**
347
- * public key of signature, which will be auto generated by plugin,
348
- * generate by `selfsigned` if not set
359
+ * Path to index file that make {@link startupWithUpdater} as default export
360
+ *
361
+ * Generate from plugin configuration by default
349
362
  */
350
- SIGNATURE_CERT?: string
363
+ mainPath?: string
351
364
  /**
352
- * repository url, e.g. `https://github.com/electron/electron`
353
- *
354
- * you can use the `repository` in `package.json`
355
- *
356
- * if `updateJsonURL` or `releaseAsarURL` are absent,
357
- * `repository` will be used to determine the url
365
+ * Updater options
366
+ */
367
+ updater?: (() => Promisable<Updater>) | UpdaterOption
368
+ /**
369
+ * Hooks on rename temp asar path to `${app.name}.asar`
358
370
  */
359
- repository?: string
371
+ onInstall?: OnInstallFunction
360
372
  /**
361
- * URL of version info json
362
- * @default `${repository.replace('github.com', 'raw.githubusercontent.com')}/master/version.json`
363
- * @throws if `updateJsonURL` and `repository` are all not set
373
+ * Hooks before app startup
374
+ * @param mainFilePath main file path of `${app.name}.asar`
375
+ * @param logger logger
364
376
  */
365
- updateJsonURL?: string
377
+ beforeStart?: (mainFilePath: string, logger?: Logger) => Promisable<void>
366
378
  /**
367
- * URL of release asar.gz
368
- * @default `${repository}/releases/download/v${version}/${app.name}-${version}.asar.gz`
369
- * @throws if `releaseAsarURL` and `repository` are all not set
379
+ * Hooks on app startup error
380
+ * @param err installing or startup error
381
+ * @param logger logger
370
382
  */
371
- releaseAsarURL?: string
383
+ onStartError?: (err: unknown, logger?: Logger) => void
384
+ }
385
+ /**
386
+ * Hooks on rename temp asar path to `${app.name}.asar`
387
+ * @param install `() => renameSync(tempAsarPath, appNameAsarPath)`
388
+ * @param tempAsarPath temp(updated) asar path
389
+ * @param appNameAsarPath `${app.name}.asar` path
390
+ * @param logger logger
391
+ * @default install(); logger.info('update success!')
392
+ */
393
+ type OnInstallFunction = (
394
+ install: VoidFunction,
395
+ tempAsarPath: string,
396
+ appNameAsarPath: string,
397
+ logger?: Logger
398
+ ) => Promisable<void>
399
+ ```
400
+
401
+ #### Updater
402
+
403
+ ```ts
404
+ export interface UpdaterOption {
372
405
  /**
373
- * whether to receive beta update
406
+ * Update provider
407
+ *
408
+ * If you will not setup `UpdateJSON` or `Buffer` in params when checking update or download, this option is **required**
409
+ */
410
+ provider?: IProvider
411
+ /**
412
+ * Certifaction key of signature, which will be auto generated by plugin,
413
+ * generate by `selfsigned` if not set
414
+ */
415
+ SIGNATURE_CERT?: string
416
+ /**
417
+ * Whether to receive beta update
374
418
  */
375
419
  receiveBeta?: boolean
376
- overrideFunctions?: UpdaterOverrideFunctions
377
- downloadConfig?: UpdaterDownloadConfig
420
+ /**
421
+ * Updater logger
422
+ */
423
+ logger?: Logger
378
424
  }
425
+
379
426
  export type Logger = {
380
427
  info: (msg: string) => void
381
428
  debug: (msg: string) => void
382
429
  warn: (msg: string) => void
383
430
  error: (msg: string, e?: Error) => void
384
431
  }
432
+ ```
433
+ #### Provider
385
434
 
386
- export type UpdaterOverrideFunctions = {
435
+ ```ts
436
+ export type OnDownloading = (progress: DownloadingInfo) => void
437
+
438
+ export interface DownloadingInfo {
387
439
  /**
388
- * custom version compare function
389
- * @param version1 old version string
390
- * @param version2 new version string
391
- * @returns if version1 < version2
440
+ * Download buffer delta
392
441
  */
393
- isLowerVersion?: (version1: string, version2: string) => boolean | Promise<boolean>
442
+ delta: number
394
443
  /**
395
- * custom verify signature function
396
- * @param buffer file buffer
397
- * @param signature signature
398
- * @param cert certificate
399
- * @returns if signature is valid, returns the version or `true` , otherwise returns `false`
444
+ * Downloaded percent, 0 ~ 100
445
+ *
446
+ * If no `Content-Length` header, will be -1
447
+ */
448
+ percent: number
449
+ /**
450
+ * Total size
451
+ *
452
+ * If not `Content-Length` header, will be -1
400
453
  */
401
- verifySignaure?: (buffer: Buffer, signature: string, cert: string) => string | false | Promise<string | false>
454
+ total: number
402
455
  /**
403
- * custom download JSON function
404
- * @param url download url
405
- * @param header download header
406
- * @returns `UpdateJSON`
456
+ * Downloaded size
407
457
  */
408
- downloadJSON?: (url: string, headers: Record<string, any>) => Promise<UpdateJSON>
458
+ transferred: number
409
459
  /**
410
- * custom download buffer function
411
- * @param url download url
412
- * @param headers download header
413
- * @param total precaculated file total size
414
- * @param onDownloading on downloading callback
415
- * @returns `Buffer`
460
+ * Download speed, bytes per second
416
461
  */
417
- downloadBuffer?: (url: string, headers: Record<string, any>, total: number, onDownloading?: (progress: DownloadingInfo) => void) => Promise<Buffer>
462
+ bps: number
418
463
  }
419
464
 
420
- export type UpdaterDownloadConfig = {
465
+ export interface IProvider {
466
+ /**
467
+ * Provider name
468
+ */
469
+ name: string
470
+ /**
471
+ * Download update json
472
+ * @param versionPath parsed version path in project
473
+ * @param signal abort signal
474
+ */
475
+ downloadJSON: (versionPath: string, signal: AbortSignal) => Promise<UpdateJSON>
476
+ /**
477
+ * Download update asar
478
+ * @param name app name
479
+ * @param updateInfo existing update info
480
+ * @param signal abort signal
481
+ * @param onDownloading hook for on downloading
482
+ */
483
+ downloadAsar: (
484
+ name: string,
485
+ updateInfo: UpdateInfo,
486
+ signal: AbortSignal,
487
+ onDownloading?: (info: DownloadingInfo) => void
488
+ ) => Promise<Buffer>
421
489
  /**
422
- * download user agent
423
- * @default 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.183 Safari/537.36'
490
+ * Check the old version is less than new version
491
+ * @param oldVer old version string
492
+ * @param newVer new version string
424
493
  */
425
- userAgent?: string
494
+ isLowerVersion: (oldVer: string, newVer: string) => boolean
426
495
  /**
427
- * extra download header, `accept` and `user-agent` is set by default
496
+ * Function to decompress file using brotli
497
+ * @param buffer compressed file buffer
428
498
  */
429
- extraHeader?: Record<string, string>
499
+ unzipFile: (buffer: Buffer) => Promise<Buffer>
500
+ /**
501
+ * Verify asar signature,
502
+ * if signature is valid, returns the version, otherwise returns `undefined`
503
+ * @param buffer file buffer
504
+ * @param version target version
505
+ * @param signature signature
506
+ * @param cert certificate
507
+ */
508
+ verifySignaure: (buffer: Buffer, version: string, signature: string, cert: string) => Promisable<boolean>
430
509
  }
431
510
  ```
432
511
 
433
512
  #### Plugin
434
513
 
435
514
  ```ts
436
- type ElectronWithUpdaterOptions = {
515
+ export interface ElectronWithUpdaterOptions {
437
516
  /**
438
- * whether is in build mode
517
+ * Whether is in build mode
439
518
  * ```ts
440
519
  * export default defineConfig(({ command }) => {
441
520
  * const isBuild = command === 'build'
@@ -444,65 +523,77 @@ type ElectronWithUpdaterOptions = {
444
523
  */
445
524
  isBuild: boolean
446
525
  /**
447
- * manually setup package.json, read name, version and main
526
+ * Manually setup package.json, read name, version and main,
527
+ * use `local-pkg` of `loadPackageJSON()` to load package.json by default
448
528
  * ```ts
449
529
  * import pkg from './package.json'
450
530
  * ```
451
531
  */
452
532
  pkg?: PKG
453
533
  /**
454
- * whether to generate sourcemap
534
+ * Whether to generate sourcemap
535
+ * @default !isBuild
455
536
  */
456
537
  sourcemap?: boolean
457
538
  /**
458
- * whether to minify the code
539
+ * Whether to minify the code
540
+ * @default isBuild
459
541
  */
460
542
  minify?: boolean
461
543
  /**
462
- * whether to generate bytecode
544
+ * Whether to generate bytecode
463
545
  *
464
- * **only support commonjs**
546
+ * **Only support CommonJS**
465
547
  *
466
- * only main process by default, if you want to use in preload script, please use `electronWithUpdater({ bytecode: { enablePreload: true } })` and set `sandbox: false` when creating window
548
+ * Only main process by default, if you want to use in preload script, please use `electronWithUpdater({ bytecode: { enablePreload: true } })` and set `sandbox: false` when creating window
467
549
  */
468
550
  bytecode?: boolean | BytecodeOptions
469
551
  /**
470
- * use NotBundle() plugin in main
552
+ * Use `NotBundle()` plugin in main
471
553
  * @default true
472
554
  */
473
555
  useNotBundle?: boolean
556
+ /**
557
+ * Whether to generate version json
558
+ * @default isCI
559
+ */
560
+ buildVersionJson?: boolean
474
561
  /**
475
562
  * Whether to log parsed options
476
563
  *
477
- * to show certificate and private keys, set `logParsedOptions: { showKeys: true }`
564
+ * To show certificate and private keys, set `logParsedOptions: { showKeys: true }`
478
565
  */
479
566
  logParsedOptions?: boolean | { showKeys: boolean }
480
567
  /**
481
- * main options
568
+ * Main process options
569
+ *
570
+ * To change output directories, use `options.updater.paths.electronDistPath` instead
482
571
  */
483
- main: MakeRequiredAndReplaceKey<ElectronSimpleOptions['main'], 'entry', 'files'>
572
+ main: MakeRequiredAndReplaceKey<ElectronSimpleOptions['main'], 'entry', 'files'> & ExcludeOutputDirOptions
484
573
  /**
485
- * preload options
574
+ * Preload process options
575
+ *
576
+ * To change output directories, use `options.updater.paths.electronDistPath` instead
486
577
  */
487
- preload: MakeRequiredAndReplaceKey<Exclude<ElectronSimpleOptions['preload'], undefined>, 'input', 'files'>
578
+ preload: MakeRequiredAndReplaceKey<Exclude<ElectronSimpleOptions['preload'], undefined>, 'input', 'files'> & ExcludeOutputDirOptions
488
579
  /**
489
- * updater options
580
+ * Updater options
490
581
  */
491
582
  updater?: ElectronUpdaterOptions
492
583
  }
493
584
 
494
- type ElectronUpdaterOptions = {
585
+ export interface ElectronUpdaterOptions {
495
586
  /**
496
- * mini version of entry
587
+ * Minimum version of entry
497
588
  * @default '0.0.0'
498
589
  */
499
590
  minimumVersion?: string
500
591
  /**
501
- * config for entry (app.asar)
592
+ * Options for entry (app.asar)
502
593
  */
503
594
  entry?: BuildEntryOption
504
595
  /**
505
- * paths config
596
+ * Options for paths
506
597
  */
507
598
  paths?: {
508
599
  /**
@@ -536,23 +627,23 @@ type ElectronUpdaterOptions = {
536
627
  */
537
628
  keys?: {
538
629
  /**
539
- * path to the pem file that contains private key
540
- * if not ended with .pem, it will be appended
630
+ * Path to the pem file that contains private key
631
+ * If not ended with .pem, it will be appended
541
632
  *
542
- * **if `UPDATER_PK` is set, will read it instead of read from `privateKeyPath`**
633
+ * **If `UPDATER_PK` is set, will read it instead of read from `privateKeyPath`**
543
634
  * @default 'keys/private.pem'
544
635
  */
545
636
  privateKeyPath?: string
546
637
  /**
547
- * path to the pem file that contains public key
548
- * if not ended with .pem, it will be appended
638
+ * Path to the pem file that contains public key
639
+ * If not ended with .pem, it will be appended
549
640
  *
550
- * **if `UPDATER_CERT` is set, will read it instead of read from `certPath`**
641
+ * **If `UPDATER_CERT` is set, will read it instead of read from `certPath`**
551
642
  * @default 'keys/cert.pem'
552
643
  */
553
644
  certPath?: string
554
645
  /**
555
- * length of the key
646
+ * Length of the key
556
647
  * @default 2048
557
648
  */
558
649
  keyLength?: number
@@ -563,45 +654,62 @@ type ElectronUpdaterOptions = {
563
654
  */
564
655
  certInfo?: {
565
656
  /**
566
- * the subject of the certificate
657
+ * The subject of the certificate
567
658
  *
568
659
  * @default { commonName: `${app.name}`, organizationName: `org.${app.name}` }
569
660
  */
570
661
  subject?: DistinguishedName
571
662
  /**
572
- * expire days of the certificate
663
+ * Expire days of the certificate
573
664
  *
574
665
  * @default 3650
575
666
  */
576
667
  days?: number
577
668
  }
578
- overrideGenerator?: GeneratorOverrideFunctions
579
669
  }
670
+ overrideGenerator?: GeneratorOverrideFunctions
671
+ }
672
+
673
+ export interface BytecodeOptions {
674
+ enable: boolean
675
+ /**
676
+ * Enable in preload script. Remember to set `sandbox: false` when creating window
677
+ */
678
+ preload?: boolean
679
+ /**
680
+ * Custom electron binary path
681
+ */
682
+ electronPath?: string
683
+ /**
684
+ * Before transformed code compile function. If return `Falsy` value, it will be ignored
685
+ * @param code transformed code
686
+ * @param id file path
687
+ */
688
+ beforeCompile?: (code: string, id: string) => Promisable<string | null | undefined | void>
580
689
  }
581
690
 
582
- type BuildEntryOption = {
691
+ export interface BuildEntryOption {
583
692
  /**
584
- * whether to minify
693
+ * Override to minify on entry
585
694
  * @default isBuild
586
695
  */
587
696
  minify?: boolean
588
697
  /**
589
- * whether to generate sourcemap
590
- * @default isBuild
698
+ * Override to generate sourcemap on entry
591
699
  */
592
700
  sourcemap?: boolean
593
701
  /**
594
- * path to app entry output file
702
+ * Path to app entry output file
595
703
  * @default 'dist-entry'
596
704
  */
597
705
  entryOutputDirPath?: string
598
706
  /**
599
- * path to app entry file
707
+ * Path to app entry file
600
708
  * @default 'electron/entry.ts'
601
709
  */
602
710
  appEntryPath?: string
603
711
  /**
604
- * esbuild path map of native modules in entry directory
712
+ * Esbuild path map of native modules in entry directory
605
713
  *
606
714
  * @default {}
607
715
  * @example
@@ -609,7 +717,7 @@ type BuildEntryOption = {
609
717
  */
610
718
  nativeModuleEntryMap?: Record<string, string>
611
719
  /**
612
- * override options for esbuild
720
+ * Custom options for esbuild
613
721
  * ```ts
614
722
  * // default options
615
723
  * const options = {
@@ -628,34 +736,32 @@ type BuildEntryOption = {
628
736
  * loader: {
629
737
  * '.node': 'empty',
630
738
  * },
631
- * define: {
632
- * __SIGNATURE_CERT__: JSON.stringify(cert),
633
- * },
739
+ * define,
634
740
  * }
635
741
  * ```
636
742
  */
637
743
  overrideEsbuildOptions?: BuildOptions
638
744
  /**
639
- * resolve extra files on startup, such as `.node`
745
+ * Resolve extra files on startup, such as `.node`
640
746
  * @remark won't trigger will reload
641
747
  */
642
748
  postBuild?: (args: {
643
749
  /**
644
- * get path from `entryOutputDirPath`
750
+ * Get path from `entryOutputDirPath`
645
751
  */
646
752
  getPathFromEntryOutputDir: (...paths: string[]) => string
647
753
  /**
648
- * check exist and copy file to `entryOutputDirPath`
754
+ * Check exist and copy file to `entryOutputDirPath`
649
755
  *
650
- * if `to` absent, set to `basename(from)`
756
+ * If `to` absent, set to `basename(from)`
651
757
  *
652
- * if `skipIfExist` absent, skip copy if `to` exist
758
+ * If `skipIfExist` absent, skip copy if `to` exist
653
759
  */
654
760
  copyToEntryOutputDir: (options: {
655
761
  from: string
656
762
  to?: string
657
763
  /**
658
- * skip copy if `to` exist
764
+ * Skip copy if `to` exist
659
765
  * @default true
660
766
  */
661
767
  skipIfExist?: boolean
@@ -663,17 +769,22 @@ type BuildEntryOption = {
663
769
  }) => Promisable<void>
664
770
  }
665
771
 
666
- type GeneratorOverrideFunctions = {
772
+ export interface GeneratorOverrideFunctions {
667
773
  /**
668
- * custom signature generate function
774
+ * Custom signature generate function
669
775
  * @param buffer file buffer
670
776
  * @param privateKey private key
671
777
  * @param cert certificate string, **EOL must be '\n'**
672
778
  * @param version current version
673
779
  */
674
- generateSignature?: (buffer: Buffer, privateKey: string, cert: string, version: string) => string | Promise<string>
780
+ generateSignature?: (
781
+ buffer: Buffer,
782
+ privateKey: string,
783
+ cert: string,
784
+ version: string
785
+ ) => Promisable<string>
675
786
  /**
676
- * custom generate version json function
787
+ * Custom generate version json function
677
788
  * @param existingJson The existing JSON object.
678
789
  * @param buffer file buffer
679
790
  * @param signature generated signature
@@ -681,10 +792,27 @@ type GeneratorOverrideFunctions = {
681
792
  * @param minVersion The minimum version
682
793
  * @returns The updated version json
683
794
  */
684
- generateVersionJson?: (existingJson: UpdateJSON, buffer: Buffer, signature: string, version: string, minVersion: string) => UpdateJSON | Promise<UpdateJSON>
795
+ generateVersionJson?: (
796
+ existingJson: UpdateJSON,
797
+ signature: string,
798
+ version: string,
799
+ minVersion: string
800
+ ) => Promisable<UpdateJSON>
801
+ /**
802
+ * Custom generate zip file buffer
803
+ * @param buffer source buffer
804
+ */
805
+ generateGzipFile?: (buffer: Buffer) => Promisable<Buffer>
685
806
  }
686
807
  ```
687
808
 
809
+ ## Credits
810
+
811
+ - [Obsidian](https://obsidian.md/) for upgrade strategy
812
+ - [vite-plugin-electron](https://github.com/electron-vite/vite-plugin-electron) for vite plugin
813
+ - [electron-builder](https://github.com/electron-userland/electron-builder) for update api
814
+ - [electron-vite](https://github.com/alex8088/electron-vite) for bytecode plugin inspiration
815
+
688
816
  ## License
689
817
 
690
818
  MIT