electron-incremental-update 1.0.2 → 1.1.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/README.md CHANGED
@@ -1,301 +1,549 @@
1
- ## Electron Incremental Updater
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.
4
-
5
- There will be two asar in production, `app.asar` and `${name}.asar` (`electron.app.name`, also as the `name` field in `package.json`).
6
-
7
- The `app.asar` is used to load `${name}.asar` and initialize the `Updater`.
8
-
9
- The new `${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 `${name}.asar` will be replaced by the new one. Hooks like `beforeDoUpdate` are provided.
10
-
11
- All **native modules** should be packaged into `app.asar` to reduce `${name}.asar` file size, [see usage](#use-native-modules)
12
-
13
- no `vite-plugin-electron-renderer` config
14
-
15
- - inspired by [Obsidian](https://obsidian.md/)'s upgrade strategy
16
-
17
- ## Install
18
-
19
- ### npm
20
- ```bash
21
- npm install -D vite-plugin-electron electron-incremental-update
22
- ```
23
- ### yarn
24
- ```bash
25
- yarn add -D vite-plugin-electron electron-incremental-update
26
- ```
27
- ### pnpm
28
- ```bash
29
- pnpm add -D vite-plugin-electron electron-incremental-update
30
- ```
31
-
32
- ## Getting started
33
-
34
- ### Project structure
35
-
36
- base on [electron-vite-vue](https://github.com/electron-vite/electron-vite-vue)
37
-
38
- ```
39
- electron
40
- ├── entry.ts // <- entry file
41
- ├── main
42
- │ └── index.ts
43
- ├── preload
44
- │ └── index.ts
45
- └── native // possible native modules
46
- └── index.ts
47
- src
48
- └── ...
49
- ```
50
-
51
- ### Setup entry
52
-
53
- in `electron/entry.ts` (build by `Esbuild`)
54
-
55
- ```ts
56
- import { initApp } from 'electron-incremental-update'
57
- import { parseGithubCdnURL } from 'electron-incremental-update/utils'
58
- import { repository } from '../package.json'
59
-
60
- const SIGNATURE_CERT = '' // auto generate certificate when start app
61
-
62
- initApp({ onStart: console.log })
63
- .setUpdater({
64
- SIGNATURE_CERT,
65
- // repository,
66
- // updateJsonURL: parseGithubCdnURL(repository, 'https://your.cdn.url/', 'version.json'),
67
- // releaseAsarURL: parseGithubCdnURL(repository, 'https://your.cdn.url/', `download/latest/${name}.asar.gz`),
68
- // receiveBeta: true
69
- })
70
- ```
71
-
72
- - [some CDN resources](https://github.com/XIU2/UserScript/blob/master/GithubEnhanced-High-Speed-Download.user.js#L34):
73
-
74
- ### Setup `vite.config.ts`
75
-
76
- All options are documented with JSDoc
77
-
78
- - cert will read from `process.env.UPDATER_CERT` first, if absend, read config
79
- - privatekey will read from `process.env.UPDATER_PK` first, if absend, read config
80
-
81
- in `vite.config.mts`
82
-
83
- ```ts
84
- import { defineConfig } from 'vite'
85
- import { debugStartup, electronWithUpdater } from 'electron-incremental-update/vite'
86
- import pkg from './package.json'
87
-
88
- export default defineConfig(async ({ command }) => {
89
- const isBuild = command === 'build'
90
- return {
91
- plugins: [
92
- electronWithUpdater({
93
- pkg,
94
- isBuild,
95
- logParsedOptions: true,
96
- main: {
97
- files: ['./electron/main/index.ts', './electron/main/worker.ts'],
98
- // see https://github.com/electron-vite/electron-vite-vue/blob/85ed267c4851bf59f32888d766c0071661d4b94c/vite.config.ts#L22-L28
99
- onstart: debugStartup,
100
- },
101
- preload: {
102
- files: './electron/preload/index.ts',
103
- },
104
- updater: {
105
- // options
106
- }
107
- }),
108
- ],
109
- server: process.env.VSCODE_DEBUG && (() => {
110
- const url = new URL(pkg.debug.env.VITE_DEV_SERVER_URL)
111
- return {
112
- host: url.hostname,
113
- port: +url.port,
114
- }
115
- })(),
116
- }
117
- })
118
- ```
119
-
120
- ### Modify package.json
121
-
122
- ```json
123
- {
124
- "main": "dist-entry/entry.js" // <- entry file path
125
- }
126
- ```
127
-
128
- ### Config electron-builder
129
-
130
- ```js
131
- const { name } = require('./package.json')
132
-
133
- const targetFile = `${name}.asar`
134
- /**
135
- * @type {import('electron-builder').Configuration}
136
- */
137
- module.exports = {
138
- appId: 'YourAppID',
139
- productName: name,
140
- files: [
141
- // entry files
142
- 'dist-entry',
143
- ],
144
- npmRebuild: false,
145
- asarUnpack: [
146
- '**/*.{node,dll,dylib,so}',
147
- ],
148
- directories: {
149
- output: 'release',
150
- },
151
- extraResources: [
152
- { from: `release/${targetFile}`, to: targetFile }, // <- asar file
153
- ],
154
- // disable publish
155
- publish: null,
156
- }
157
- ```
158
-
159
- ## Usage
160
-
161
- ### use in main process
162
-
163
- To use electron's `net` module for updating, the `checkUpdate` and `download` functions must be called after the app is ready by default.
164
-
165
- However, you have the option to customize the download function when creating the updater.
166
-
167
- **NOTE: There should only one function and should be default export in the entry file**
168
-
169
- in `electron/main/index.ts`
170
-
171
- ```ts
172
- import { startupWithUpdater } from 'electron-incremental-update'
173
- import { getPathFromAppNameAsar, getVersions } from 'electron-incremental-update/utils'
174
- import { app } from 'electron'
175
-
176
- export default startupWithUpdater((updater) => {
177
- await app.whenReady()
178
-
179
- const { appVersion, electronVersion, entryVersion } = getVersions()
180
- console.log(`${app.name}.asar path`, getPathFromAppNameAsar())
181
- console.log('app version:', appVersion)
182
- console.log('entry (installer) version', entryVersion)
183
- console.log('electron version', electronVersion)
184
-
185
- updater.onDownloading = ({ percent }) => {
186
- console.log(percent)
187
- }
188
- updater.logger = console
189
- updater.checkUpdate().then(async (result) => {
190
- if (result === undefined) {
191
- console.log('Update Unavailable')
192
- } else if (result instanceof Error) {
193
- console.error(result)
194
- } else {
195
- console.log('new version: ', result.version)
196
- const { response } = await dialog.showMessageBox({
197
- type: 'info',
198
- buttons: ['Download', 'Later'],
199
- message: 'Application update available!',
200
- })
201
- if (response !== 0) {
202
- return
203
- }
204
- const downloadResult = await updater.download()
205
- if (downloadResult) {
206
- updater.quitAndInstall()
207
- }
208
- }
209
- })
210
- })
211
- ```
212
-
213
- ### use native modules
214
-
215
- All the **native modules** should be set as `dependency` in `package.json`. `electron-rebuild` only check dependencies inside `dependency` field.
216
-
217
- 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. You can setup `nativeModuleEntryMap` option to prebundle all the native modules and skip bundled by `electron-builder`
218
-
219
- in `vite.config.ts`
220
-
221
- ```ts
222
- const plugin = electronWithUpdater({
223
- // options...
224
- updater: {
225
- entry: {
226
- nativeModuleEntryMap: {
227
- db: './electron/native/db.ts',
228
- },
229
- postBuild: async ({ existsAndCopyToEntryOutputDir }) => {
230
- // for better-sqlite3
231
- existsAndCopyToEntryOutputDir({
232
- from: './node_modules/better-sqlite3/build/Release/better_sqlite3.node',
233
- skipIfExist: false,
234
- })
235
- // for @napi-rs/image
236
- const startStr = '@napi-rs+image-'
237
- const fileName = (await readdir('./node_modules/.pnpm')).filter(p => p.startsWith(startStr))[0]
238
- const archName = fileName.substring(startStr.length).split('@')[0]
239
- existsAndCopyToEntryOutputDir({
240
- from: `./node_modules/.pnpm/${fileName}/node_modules/@napi-rs/image-${archName}/image.${archName}.node`,
241
- })
242
- },
243
- },
244
- },
245
- })
246
- ```
247
-
248
- in `electron/native/db.ts`
249
-
250
- ```ts
251
- import Database from 'better-sqlite3'
252
- import { getPaths } from 'electron-incremental-update/utils'
253
-
254
- const db = new Database(':memory:', { nativeBinding: getPaths().getPathFromEntryAsar('better_sqlite3.node') })
255
-
256
- export function test() {
257
- db.exec(
258
- 'DROP TABLE IF EXISTS employees; '
259
- + 'CREATE TABLE IF NOT EXISTS employees (name TEXT, salary INTEGER)',
260
- )
261
-
262
- db.prepare('INSERT INTO employees VALUES (:n, :s)').run({
263
- n: 'James',
264
- s: 5000,
265
- })
266
-
267
- const r = db.prepare('SELECT * from employees').all()
268
- console.log(r)
269
- // [ { name: 'James', salary: 50000 } ]
270
-
271
- db.close()
272
- }
273
- ```
274
-
275
- in `electron/main/service.ts`
276
-
277
- ```ts
278
- import { loadNativeModuleFromEntry } from 'electron-incremental-update/utils'
279
-
280
- const requireNative = loadNativeModuleFromEntry()
281
-
282
- requireNative<typeof import('../native/db')>('db').test()
283
- ```
284
-
285
- in `electron-builder.config.js`
286
-
287
- ```js
288
- module.exports = {
289
- files: [
290
- 'dist-entry',
291
- // exclude better-sqlite3 from electron-builder
292
- '!node_modules/better-sqlite3/**',
293
- // exclude @napi-rs/image from electron-builder
294
- '!node_modules/@napi-rs*/**',
295
- ]
296
- }
297
- ```
298
-
299
- ## License
300
-
301
- MIT
1
+ ## Electron Incremental Updater
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.
4
+
5
+ There will be two asar in production, `app.asar` and `${electron.app.name}.asar` (also as the `name` field in `package.json`).
6
+
7
+ The `app.asar` is used to load `${electron.app.name}.asar` and initialize the `Updater`.
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.
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.
12
+
13
+ no `vite-plugin-electron-renderer` config
14
+
15
+ - inspired by [Obsidian](https://obsidian.md/)'s upgrade strategy
16
+
17
+ ## Install
18
+
19
+ ### npm
20
+ ```bash
21
+ npm install -D vite-plugin-electron electron-incremental-update
22
+ ```
23
+ ### yarn
24
+ ```bash
25
+ yarn add -D vite-plugin-electron electron-incremental-update
26
+ ```
27
+ ### pnpm
28
+ ```bash
29
+ pnpm add -D vite-plugin-electron electron-incremental-update
30
+ ```
31
+
32
+ ## Getting started
33
+
34
+ ### Project structure
35
+
36
+ base on [electron-vite-vue](https://github.com/electron-vite/electron-vite-vue)
37
+
38
+ ```
39
+ electron
40
+ ├── entry.ts // <- entry file
41
+ ├── main
42
+ │ └── index.ts
43
+ ├── preload
44
+ │ └── index.ts
45
+ └── native // <- possible native modules
46
+ └── index.ts
47
+ src
48
+ └── ...
49
+ ```
50
+
51
+ ### Setup entry
52
+
53
+ in `electron/entry.ts` (build by `Esbuild`)
54
+
55
+ ```ts
56
+ import { initApp } from 'electron-incremental-update'
57
+ import { parseGithubCdnURL } from 'electron-incremental-update/utils'
58
+ import { repository } from '../package.json'
59
+
60
+ const SIGNATURE_CERT = '' // auto generate certificate when start app
61
+
62
+ initApp({ onStart: console.log })
63
+ .setUpdater({
64
+ SIGNATURE_CERT,
65
+ // repository,
66
+ // updateJsonURL: parseGithubCdnURL(repository, 'https://your.cdn.url/', 'version.json'),
67
+ // releaseAsarURL: parseGithubCdnURL(repository, 'https://your.cdn.url/', `download/latest/${name}.asar.gz`),
68
+ // receiveBeta: true
69
+ })
70
+ ```
71
+
72
+ - [some CDN resources](https://github.com/XIU2/UserScript/blob/master/GithubEnhanced-High-Speed-Download.user.js#L34):
73
+
74
+ ### Setup `vite.config.ts`
75
+
76
+ All options are documented with JSDoc
77
+
78
+ - certificate will read from `process.env.UPDATER_CERT` first, if absend, read config
79
+ - privatekey will read from `process.env.UPDATER_PK` first, if absend, read config
80
+
81
+ in `vite.config.mts`
82
+
83
+ ```ts
84
+ import { defineConfig } from 'vite'
85
+ import { debugStartup, electronWithUpdater } from 'electron-incremental-update/vite'
86
+ import pkg from './package.json'
87
+
88
+ export default defineConfig(async ({ command }) => {
89
+ const isBuild = command === 'build'
90
+ return {
91
+ plugins: [
92
+ electronWithUpdater({
93
+ pkg,
94
+ isBuild,
95
+ logParsedOptions: true,
96
+ main: {
97
+ files: ['./electron/main/index.ts', './electron/main/worker.ts'],
98
+ // see https://github.com/electron-vite/electron-vite-vue/blob/85ed267c4851bf59f32888d766c0071661d4b94c/vite.config.ts#L22-L28
99
+ onstart: debugStartup,
100
+ },
101
+ preload: {
102
+ files: './electron/preload/index.ts',
103
+ },
104
+ updater: {
105
+ // options
106
+ }
107
+ }),
108
+ ],
109
+ server: process.env.VSCODE_DEBUG && (() => {
110
+ const url = new URL(pkg.debug.env.VITE_DEV_SERVER_URL)
111
+ return {
112
+ host: url.hostname,
113
+ port: +url.port,
114
+ }
115
+ })(),
116
+ }
117
+ })
118
+ ```
119
+
120
+ ### Modify package.json
121
+
122
+ ```json
123
+ {
124
+ "main": "dist-entry/entry.js" // <- entry file path
125
+ }
126
+ ```
127
+
128
+ ### Config electron-builder
129
+
130
+ ```js
131
+ const { name } = require('./package.json')
132
+
133
+ const targetFile = `${name}.asar`
134
+ /**
135
+ * @type {import('electron-builder').Configuration}
136
+ */
137
+ module.exports = {
138
+ appId: 'YourAppID',
139
+ productName: name,
140
+ files: [
141
+ // entry files
142
+ 'dist-entry',
143
+ ],
144
+ npmRebuild: false,
145
+ asarUnpack: [
146
+ '**/*.{node,dll,dylib,so}',
147
+ ],
148
+ directories: {
149
+ output: 'release',
150
+ },
151
+ extraResources: [
152
+ { from: `release/${targetFile}`, to: targetFile }, // <- asar file
153
+ ],
154
+ // disable publish
155
+ publish: null,
156
+ }
157
+ ```
158
+
159
+ ## Usage
160
+
161
+ ### Use in main process
162
+
163
+ To use electron's `net` module for updating, the `checkUpdate` and `download` functions must be called after the app is ready by default.
164
+
165
+ However, you have the option to customize the download function when creating the updater.
166
+
167
+ **NOTE: There should only one function and should be default export in the entry file**
168
+
169
+ in `electron/main/index.ts`
170
+
171
+ ```ts
172
+ import { startupWithUpdater } from 'electron-incremental-update'
173
+ import { getPathFromAppNameAsar, getVersions } from 'electron-incremental-update/utils'
174
+ import { app } from 'electron'
175
+
176
+ export default startupWithUpdater((updater) => {
177
+ await app.whenReady()
178
+
179
+ const { appVersion, electronVersion, entryVersion } = getVersions()
180
+ console.log(`${app.name}.asar path`, getPathFromAppNameAsar())
181
+ console.log('app version:', appVersion)
182
+ console.log('entry (installer) version', entryVersion)
183
+ console.log('electron version', electronVersion)
184
+
185
+ updater.onDownloading = ({ percent }) => {
186
+ console.log(percent)
187
+ }
188
+ updater.logger = console
189
+ updater.checkUpdate().then(async (result) => {
190
+ if (result === undefined) {
191
+ console.log('Update Unavailable')
192
+ } else if (result instanceof Error) {
193
+ console.error(result)
194
+ } else {
195
+ console.log('new version: ', result.version)
196
+ const { response } = await dialog.showMessageBox({
197
+ type: 'info',
198
+ buttons: ['Download', 'Later'],
199
+ message: 'Application update available!',
200
+ })
201
+ if (response !== 0) {
202
+ return
203
+ }
204
+ const downloadResult = await updater.download()
205
+ if (downloadResult) {
206
+ updater.quitAndInstall()
207
+ }
208
+ }
209
+ })
210
+ })
211
+ ```
212
+
213
+ ### Use native modules
214
+
215
+ All the **native modules** should be set as `dependency` in `package.json`. `electron-rebuild` only check dependencies inside `dependency` field.
216
+
217
+ 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. You can setup `nativeModuleEntryMap` option to prebundle all the native modules and skip bundled by `electron-builder`
218
+
219
+ in `vite.config.ts`
220
+
221
+ ```ts
222
+ const plugin = electronWithUpdater({
223
+ // options...
224
+ updater: {
225
+ entry: {
226
+ nativeModuleEntryMap: {
227
+ db: './electron/native/db.ts',
228
+ },
229
+ postBuild: async ({ copyToEntryOutputDir }) => {
230
+ // for better-sqlite3
231
+ copyToEntryOutputDir({
232
+ from: './node_modules/better-sqlite3/build/Release/better_sqlite3.node',
233
+ skipIfExist: false,
234
+ })
235
+ // for @napi-rs/image
236
+ const startStr = '@napi-rs+image-'
237
+ const fileName = (await readdir('./node_modules/.pnpm')).filter(p => p.startsWith(startStr))[0]
238
+ const archName = fileName.substring(startStr.length).split('@')[0]
239
+ copyToEntryOutputDir({
240
+ from: `./node_modules/.pnpm/${fileName}/node_modules/@napi-rs/image-${archName}/image.${archName}.node`,
241
+ })
242
+ },
243
+ },
244
+ },
245
+ })
246
+ ```
247
+
248
+ in `electron/native/db.ts`
249
+
250
+ ```ts
251
+ import Database from 'better-sqlite3'
252
+ import { getPaths } from 'electron-incremental-update/utils'
253
+
254
+ const db = new Database(':memory:', { nativeBinding: getPaths().getPathFromEntryAsar('better_sqlite3.node') })
255
+
256
+ export function test() {
257
+ db.exec(
258
+ 'DROP TABLE IF EXISTS employees; '
259
+ + 'CREATE TABLE IF NOT EXISTS employees (name TEXT, salary INTEGER)',
260
+ )
261
+
262
+ db.prepare('INSERT INTO employees VALUES (:n, :s)').run({
263
+ n: 'James',
264
+ s: 5000,
265
+ })
266
+
267
+ const r = db.prepare('SELECT * from employees').all()
268
+ console.log(r)
269
+ // [ { name: 'James', salary: 50000 } ]
270
+
271
+ db.close()
272
+ }
273
+ ```
274
+
275
+ in `electron/main/service.ts`
276
+
277
+ ```ts
278
+ import { loadNativeModuleFromEntry } from 'electron-incremental-update/utils'
279
+
280
+ const requireNative = loadNativeModuleFromEntry()
281
+
282
+ requireNative<typeof import('../native/db')>('db').test()
283
+ ```
284
+
285
+ in `electron-builder.config.js`
286
+
287
+ ```js
288
+ module.exports = {
289
+ files: [
290
+ 'dist-entry',
291
+ // exclude dependencies in electron-builder config
292
+ '!node_modules/**',
293
+ ]
294
+ }
295
+ ```
296
+
297
+ ### Bytecode protection
298
+
299
+ WIP
300
+
301
+ plan to use [electron-vite](https://github.com/alex8088/electron-vite/blob/master/src/plugins/bytecode.ts), but fail to load the default function in `${electron.app.name}.asar/dist-electron/index.js`.
302
+
303
+ try to wrap with [`Module.wrap`](https://github.com/bytenode/bytenode?tab=readme-ov-file#bytenodecompileelectroncodejavascriptcode-options--promisebuffer), but still fail.
304
+
305
+ ### Types
306
+
307
+ ```ts
308
+ type ElectronWithUpdaterOptions = {
309
+ /**
310
+ * whether is in build mode
311
+ * ```ts
312
+ * export default defineConfig(({ command }) => {
313
+ * const isBuild = command === 'build'
314
+ * })
315
+ * ```
316
+ */
317
+ isBuild: boolean
318
+ /**
319
+ * manullay setup package.json, read name, version and main
320
+ * ```ts
321
+ * import pkg from './package.json'
322
+ * ```
323
+ */
324
+ pkg?: PKG
325
+ /**
326
+ * whether to generate sourcemap
327
+ */
328
+ sourcemap?: boolean
329
+ /**
330
+ * whether to minify the code
331
+ */
332
+ minify?: boolean
333
+ /**
334
+ * use NotBundle() plugin in main
335
+ * @default true
336
+ */
337
+ useNotBundle?: boolean
338
+ /**
339
+ * Whether to log parsed options
340
+ */
341
+ logParsedOptions?: boolean
342
+ /**
343
+ * main options
344
+ */
345
+ main: MakeRequiredAndReplaceKey<ElectronSimpleOptions['main'], 'entry', 'files'>
346
+ /**
347
+ * preload options
348
+ */
349
+ preload: MakeRequiredAndReplaceKey<Exclude<ElectronSimpleOptions['preload'], undefined>, 'input', 'files'>
350
+ /**
351
+ * updater options
352
+ */
353
+ updater?: ElectronUpdaterOptions
354
+ }
355
+
356
+ type ElectronUpdaterOptions = {
357
+ /**
358
+ * mini version of entry
359
+ * @default '0.0.0'
360
+ */
361
+ minimumVersion?: string
362
+ /**
363
+ * config for entry (app.asar)
364
+ */
365
+ entry?: BuildEntryOption
366
+ /**
367
+ * paths config
368
+ */
369
+ paths?: {
370
+ /**
371
+ * Path to asar file
372
+ * @default `release/${app.name}.asar`
373
+ */
374
+ asarOutputPath?: string
375
+ /**
376
+ * Path to version info output, content is {@link UpdateJSON}
377
+ * @default `version.json`
378
+ */
379
+ versionPath?: string
380
+ /**
381
+ * Path to gzipped asar file
382
+ * @default `release/${app.name}-${version}.asar.gz`
383
+ */
384
+ gzipPath?: string
385
+ /**
386
+ * Path to electron build output
387
+ * @default `dist-electron`
388
+ */
389
+ electronDistPath?: string
390
+ /**
391
+ * Path to renderer build output
392
+ * @default `dist`
393
+ */
394
+ rendererDistPath?: string
395
+ }
396
+ /**
397
+ * signature config
398
+ */
399
+ keys?: {
400
+ /**
401
+ * path to the pem file that contains private key
402
+ * if not ended with .pem, it will be appended
403
+ *
404
+ * **if `UPDATER_PK` is set, will read it instead of read from `privateKeyPath`**
405
+ * @default 'keys/private.pem'
406
+ */
407
+ privateKeyPath?: string
408
+ /**
409
+ * path to the pem file that contains public key
410
+ * if not ended with .pem, it will be appended
411
+ *
412
+ * **if `UPDATER_CERT` is set, will read it instead of read from `certPath`**
413
+ * @default 'keys/cert.pem'
414
+ */
415
+ certPath?: string
416
+ /**
417
+ * length of the key
418
+ * @default 2048
419
+ */
420
+ keyLength?: number
421
+ /**
422
+ * X509 certificate info
423
+ *
424
+ * only generate simple **self-signed** certificate **without extensions**
425
+ */
426
+ certInfo?: {
427
+ /**
428
+ * the subject of the certificate
429
+ *
430
+ * @default { commonName: `${app.name}`, organizationName: `org.${app.name}` }
431
+ */
432
+ subject?: DistinguishedName
433
+ /**
434
+ * expire days of the certificate
435
+ *
436
+ * @default 3650
437
+ */
438
+ days?: number
439
+ }
440
+ overrideGenerator?: GeneratorOverrideFunctions
441
+ }
442
+ }
443
+
444
+ type BuildEntryOption = {
445
+ /**
446
+ * whether to minify
447
+ * @default isBuild
448
+ */
449
+ minify?: boolean
450
+ /**
451
+ * whether to generate sourcemap
452
+ * @default isBuild
453
+ */
454
+ sourcemap?: boolean
455
+ /**
456
+ * path to app entry output file
457
+ * @default 'dist-entry'
458
+ */
459
+ entryOutputDirPath?: string
460
+ /**
461
+ * path to app entry file
462
+ * @default 'electron/entry.ts'
463
+ */
464
+ appEntryPath?: string
465
+ /**
466
+ * esbuild path map of native modules in entry directory
467
+ *
468
+ * @default {}
469
+ * @example
470
+ * { db: './electron/native/db.ts' }
471
+ */
472
+ nativeModuleEntryMap?: Record<string, string>
473
+ /**
474
+ * custom options for esbuild
475
+ * ```ts
476
+ * // default options
477
+ * const options = {
478
+ * entryPoints: {
479
+ * entry: appEntryPath,
480
+ * ...moduleEntryMap,
481
+ * },
482
+ * bundle: true,
483
+ * platform: 'node',
484
+ * outdir: entryOutputDirPath,
485
+ * minify,
486
+ * sourcemap,
487
+ * entryNames: '[dir]/[name]',
488
+ * assetNames: '[dir]/[name]',
489
+ * external: ['electron', 'original-fs'],
490
+ * loader: {
491
+ * '.node': 'empty',
492
+ * },
493
+ * }
494
+ * ```
495
+ */
496
+ overrideEsbuildOptions?: BuildOptions
497
+ /**
498
+ * resolve extra files on startup, such as `.node`
499
+ * @remark won't trigger will reload
500
+ */
501
+ postBuild?: (args: {
502
+ /**
503
+ * get path from `entryOutputDirPath`
504
+ */
505
+ getPathFromEntryOutputDir: (...paths: string[]) => string
506
+ /**
507
+ * check exist and copy file to `entryOutputDirPath`
508
+ *
509
+ * if `to` absent, set to `basename(from)`
510
+ *
511
+ * if `skipIfExist` absent, skip copy if `to` exist
512
+ */
513
+ copyToEntryOutputDir: (options: {
514
+ from: string
515
+ to?: string
516
+ /**
517
+ * skip copy if `to` exist
518
+ * @default true
519
+ */
520
+ skipIfExist?: boolean
521
+ }) => void
522
+ }) => Promisable<void>
523
+ }
524
+
525
+ type GeneratorOverrideFunctions = {
526
+ /**
527
+ * custom signature generate function
528
+ * @param buffer file buffer
529
+ * @param privateKey private key
530
+ * @param cert certificate string, **EOL must be '\n'**
531
+ * @param version current version
532
+ */
533
+ generateSignature?: (buffer: Buffer, privateKey: string, cert: string, version: string) => string | Promise<string>
534
+ /**
535
+ * custom generate version json function
536
+ * @param existingJson The existing JSON object.
537
+ * @param buffer file buffer
538
+ * @param signature generated signature
539
+ * @param version current version
540
+ * @param minVersion The minimum version
541
+ * @returns The updated version json
542
+ */
543
+ generateVersionJson?: (existingJson: UpdateJSON, buffer: Buffer, signature: string, version: string, minVersion: string) => UpdateJSON | Promise<UpdateJSON>
544
+ }
545
+ ```
546
+
547
+ ## License
548
+
549
+ MIT