electron-incremental-update 0.9.1 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,137 +1,136 @@
1
- ## electron incremental updater
1
+ ## Electron Incremental Updater
2
2
 
3
- This project provide a vite plugin, `Updater` class and some useful functions to generate incremental update.
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
4
 
5
- There will be two asar in production, `app.asar` and `main.asar` (if "main" is your app's name).
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
6
 
7
- The `app.asar` is used to load `main.asar` and initialize the `updater`. Also, all the **native modules**, which are set as `dependencies` in `package.json`, will be packaged into `app.asar` by `electron-builder`, [see usage](#use-native-modules).
7
+ The `app.asar` is used to load `${name}.asar` and initialize the `Updater`.
8
8
 
9
- The new `main.asar` downloaded from remote will be verified by presigned RSA + Signature. When pass the check and restart, the old `main.asar` will be replaced by the new one. Hooks like `beforeDoUpdate` are provided.
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
10
 
11
- - inspired by Obsidian's update strategy
11
+ All **native modules** should be packaged into `app.asar` to reduce `${name}.asar` file size, [see usage](#use-native-modules)
12
12
 
13
- ### notice
13
+ no `vite-plugin-electron-renderer` config
14
14
 
15
- - this plugin is developed with [vite-plugin-electron](https://github.com/electron-vite/vite-plugin-electron), and may be effect in other electron vite frameworks
16
- - **all options are documented in the jsdoc**
15
+ - inspired by [Obsidian](https://obsidian.md/)'s upgrade strategy
17
16
 
18
- ## install
17
+ ## Install
19
18
 
20
19
  ### npm
21
20
  ```bash
22
- npm install electron-incremental-update
21
+ npm install -D vite-plugin-electron electron-incremental-update
23
22
  ```
24
23
  ### yarn
25
24
  ```bash
26
- yarn add electron-incremental-update
25
+ yarn add -D vite-plugin-electron electron-incremental-update
27
26
  ```
28
27
  ### pnpm
29
28
  ```bash
30
- pnpm add electron-incremental-update
29
+ pnpm add -D vite-plugin-electron electron-incremental-update
31
30
  ```
32
31
 
33
- ## setup
32
+ ## Getting started
33
+
34
+ ### Project structure
34
35
 
35
36
  base on [electron-vite-vue](https://github.com/electron-vite/electron-vite-vue)
36
37
 
37
38
  ```
38
39
  electron
39
- ├── app.ts // <- add app entry file
40
- ├── electron-env.d.ts
40
+ ├── entry.ts // <- entry file
41
41
  ├── main
42
- ├── db.ts
43
- ├── index.ts
44
- └── preload
42
+ └── index.ts
43
+ ├── preload
44
+ └── index.ts
45
+ └── native // possible native modules
45
46
  └── index.ts
46
47
  src
47
48
  └── ...
48
49
  ```
49
50
 
50
- ### setup app
51
+ ### Setup entry
52
+
53
+ in `electron/entry.ts` (build by `Esbuild`)
51
54
 
52
55
  ```ts
53
- // electron/app.ts
54
- import { initApp, parseGithubCdnURL } from 'electron-incremental-update'
55
- import { name, repository } from '../package.json'
56
+ import { initApp } from 'electron-incremental-update'
57
+ import { parseGithubCdnURL } from 'electron-incremental-update/utils'
58
+ import { repository } from '../package.json'
56
59
 
57
60
  const SIGNATURE_CERT = '' // auto generate certificate when start app
58
61
 
59
62
  initApp({ onStart: console.log })
60
- // can be updater option or function that return updater
61
63
  .setUpdater({
62
64
  SIGNATURE_CERT,
63
- productName: name,
64
- repository,
65
- updateJsonURL: parseGithubCdnURL(repository, '...', 'version.json'),
66
- releaseAsarURL: parseGithubCdnURL(repository, '...', `download/latest/${name}.asar.gz`),
67
- receiveBeta: true
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
68
69
  })
69
70
  ```
70
71
 
71
- - [some cdn resources](https://github.com/XIU2/UserScript/blob/master/GithubEnhanced-High-Speed-Download.user.js#L34):
72
+ - [some CDN resources](https://github.com/XIU2/UserScript/blob/master/GithubEnhanced-High-Speed-Download.user.js#L34):
73
+
74
+ ### Setup `vite.config.ts`
72
75
 
73
- ### setup vite.config.ts
76
+ All options are documented with JSDoc
74
77
 
75
- make sure the plugin is set in the **last** build task
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
76
80
 
77
- - for `vite-plugin-electron`, set it to `preload` (the second object in the plugin option array)
78
- - cert is read from `process.env.UPDATER_CERT` first, then read config
79
- - privatekey is read from `process.env.UPDATER_PK` first, then read config
81
+ in `vite.config.mts`
80
82
 
81
83
  ```ts
82
- // vite.config.ts
83
- export default defineConfig(({ command }) => {
84
- const isBuild = command === 'build'
85
- // ...
84
+ import { defineConfig } from 'vite'
85
+ import { debugStartup, electronWithUpdater } from 'electron-incremental-update/vite'
86
+ import pkg from './package.json'
86
87
 
88
+ export default defineConfig(async ({ command }) => {
89
+ const isBuild = command === 'build'
87
90
  return {
88
91
  plugins: [
89
- electron([
90
- // main
91
- {
92
- // ...
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,
93
100
  },
94
- // preload
95
- {
96
- // ...
97
- vite: {
98
- plugins: [
99
- updater({
100
- productName: pkg.name,
101
- version: pkg.version,
102
- isBuild,
103
- }),
104
- ],
105
- // ...
106
- }
101
+ preload: {
102
+ files: './electron/preload/index.ts',
107
103
  },
108
- // when using vite-plugin-electron-renderer
109
- {
110
- // ...
104
+ updater: {
105
+ // options
111
106
  }
112
- ]),
113
- // ...
107
+ }),
114
108
  ],
115
- // ...
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
116
  }
117
117
  })
118
118
  ```
119
119
 
120
- ### modify package.json
120
+ ### Modify package.json
121
121
 
122
122
  ```json
123
123
  {
124
- // ...
125
- "main": "app.js" // <- app entry file path
124
+ "main": "dist-entry/entry.js" // <- entry file path
126
125
  }
127
126
  ```
128
127
 
129
- ### config electron-builder
128
+ ### Config electron-builder
130
129
 
131
130
  ```js
132
131
  const { name } = require('./package.json')
133
132
 
134
- const target = `${name}.asar`
133
+ const targetFile = `${name}.asar`
135
134
  /**
136
135
  * @type {import('electron-builder').Configuration}
137
136
  */
@@ -139,26 +138,21 @@ module.exports = {
139
138
  appId: 'YourAppID',
140
139
  productName: name,
141
140
  files: [
142
- 'app.js', // <- app entry file
143
- '!**/{.eslintignore,.eslintrc.cjs,.editorconfig,.prettierignore,.prettierrc.yaml,dev-app-update.yml,LICENSE,.nvmrc,.npmrc}',
144
- '!**/{tsconfig.json,tsconfig.node.json,tsconfig.web.json}',
145
- '!**/*debug*.*',
146
- '!**/*.{md,zip,map}',
147
- '!**/*.{c,cpp,h,hpp,cc,hh,cxx,hxx,gypi,gyp,sh}',
148
- '!**/.{github,vscode}',
149
- '!node_modules/**/better-sqlite3/deps/**',
141
+ // entry files
142
+ 'dist-entry',
150
143
  ],
144
+ npmRebuild: false,
151
145
  asarUnpack: [
152
- '**/*.{node,dll}',
146
+ '**/*.{node,dll,dylib,so}',
153
147
  ],
154
148
  directories: {
155
149
  output: 'release',
156
150
  },
157
151
  extraResources: [
158
- { from: `release/${target}`, to: target }, // <- asar file
152
+ { from: `release/${targetFile}`, to: targetFile }, // <- asar file
159
153
  ],
160
- publish: null, // <- disable publish
161
- // ...
154
+ // disable publish
155
+ publish: null,
162
156
  }
163
157
  ```
164
158
 
@@ -170,21 +164,24 @@ To use electron's `net` module for updating, the `checkUpdate` and `download` fu
170
164
 
171
165
  However, you have the option to customize the download function when creating the updater.
172
166
 
173
- **NOTE: There can only be one function and should be default export in the entry file**
167
+ **NOTE: There should only one function and should be default export in the entry file**
168
+
169
+ in `electron/main/index.ts`
174
170
 
175
171
  ```ts
176
- // electron/main/index.ts
177
- import type { StartupWithUpdater, Updater } from 'electron-incremental-update'
178
- import { appInfo, getAppVersion, getElectronVersion, getProductAsarPath } from 'electron-incremental-update/utils'
172
+ import { startupWithUpdater } from 'electron-incremental-update'
173
+ import { getPathFromAppNameAsar, getVersions } from 'electron-incremental-update/utils'
179
174
  import { app } from 'electron'
180
- import { name } from '../../package.json'
181
175
 
182
- const startup: StartupWithUpdater = (updater: Updater) => {
176
+ export default startupWithUpdater((updater) => {
183
177
  await app.whenReady()
184
- console.log('\ncurrent:')
185
- console.log(`\tasar path: ${getProductAsarPath(name)}`)
186
- console.log(`\tapp: ${getAppVersion(name)}`)
187
- console.log(`\telectron: ${getElectronVersion()}`)
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
+
188
185
  updater.onDownloading = ({ percent }) => {
189
186
  console.log(percent)
190
187
  }
@@ -201,41 +198,104 @@ const startup: StartupWithUpdater = (updater: Updater) => {
201
198
  buttons: ['Download', 'Later'],
202
199
  message: 'Application update available!',
203
200
  })
204
- response === 0 && console.log(await updater.download())
201
+ if (response !== 0) {
202
+ return
203
+ }
204
+ const downloadResult = await updater.download()
205
+ if (downloadResult) {
206
+ updater.quitAndInstall()
207
+ }
205
208
  }
206
209
  })
207
- }
208
- export default startup
210
+ })
209
211
  ```
210
212
 
211
213
  ### use native modules
212
214
 
213
- the native modules is packed in `app.asar`, so you cannot directly access it when in production
215
+ All the **native modules** should be set as `dependency` in `package.json`. `electron-rebuild` only check dependencies inside `dependency` field.
214
216
 
215
- to use it, you can prebundle native modules, or use `requireNative` to load.
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`
216
220
 
217
221
  ```ts
218
- // db.ts
219
- import { isNoSuchNativeModuleError, requireNative } from 'electron-incremental-update/utils'
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
+ ```
220
247
 
221
- const Database = requireNative<typeof import('better-sqlite3')>('better-sqlite3')
222
- if (isNoSuchNativeModuleError(Database)) {
223
- // ...
224
- }
225
- const db = new Database(':memory:')
226
- db.exec(
227
- 'DROP TABLE IF EXISTS employees; '
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; '
228
259
  + 'CREATE TABLE IF NOT EXISTS employees (name TEXT, salary INTEGER)',
229
- )
260
+ )
230
261
 
231
- db.prepare('INSERT INTO employees VALUES (:n, :s)').run({
232
- n: 'James',
233
- s: 50000,
234
- })
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'
235
279
 
236
- const r = db.prepare('SELECT * from employees').all()
237
- console.log(r)
238
- // [ { name: 'James', salary: 50000 } ]
280
+ const requireNative = loadNativeModuleFromEntry()
239
281
 
240
- db.close()
282
+ requireNative<typeof import('../native/db')>('db').test()
241
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
@@ -6,32 +6,6 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
6
6
  throw Error('Dynamic require of "' + x + '" is not supported');
7
7
  });
8
8
 
9
- // src/utils/version.ts
10
- function parseVersion(version) {
11
- const semver = /^(\d+)\.(\d+)\.(\d+)(?:-([a-zA-Z0-9\.-]+))?/i;
12
- const match = semver.exec(version);
13
- if (!match) {
14
- throw new TypeError(`invalid version: ${version}`);
15
- }
16
- const [major, minor, patch] = match.slice(1, 4).map(Number);
17
- const ret = {
18
- major,
19
- minor,
20
- patch,
21
- stage: "",
22
- stageVersion: -1
23
- };
24
- if (match[4]) {
25
- let [stage, _v] = match[4].split(".");
26
- ret.stage = stage;
27
- ret.stageVersion = Number(_v) || -1;
28
- }
29
- if (Number.isNaN(major) || Number.isNaN(minor) || Number.isNaN(patch) || Number.isNaN(ret.stageVersion)) {
30
- throw new TypeError(`invalid version: ${version}`);
31
- }
32
- return ret;
33
- }
34
-
35
9
  // src/utils/zip.ts
36
10
  import { existsSync, readFileSync, rmSync, writeFileSync } from "node:fs";
37
11
  import { gunzip, gzip } from "node:zlib";
@@ -67,9 +41,55 @@ async function zipFile(filePath, targetFilePath = `${filePath}.gz`) {
67
41
  });
68
42
  }
69
43
 
44
+ // src/utils/noDep.ts
45
+ function parseGithubCdnURL(originRepoURL, cdnPrefix, relativeFilePath) {
46
+ if (!originRepoURL.startsWith("https://github.com/")) {
47
+ throw new Error("origin url must start with https://github.com/");
48
+ }
49
+ originRepoURL = originRepoURL.trim().replace(/\/?$/, "/").trim();
50
+ relativeFilePath = relativeFilePath.trim().replace(/^\/|\/?$/g, "").trim();
51
+ cdnPrefix = cdnPrefix.trim().replace(/^\/?|\/?$/g, "").trim();
52
+ return originRepoURL.replace("github.com", cdnPrefix) + relativeFilePath;
53
+ }
54
+ function handleUnexpectedErrors(callback) {
55
+ process.on("uncaughtException", callback);
56
+ process.on("unhandledRejection", callback);
57
+ }
58
+ function parseVersion(version) {
59
+ const semver = /^(\d+)\.(\d+)\.(\d+)(?:-([a-zA-Z0-9\.-]+))?/i;
60
+ const match = semver.exec(version);
61
+ if (!match) {
62
+ throw new TypeError(`invalid version: ${version}`);
63
+ }
64
+ const [major, minor, patch] = match.slice(1, 4).map(Number);
65
+ const ret = {
66
+ major,
67
+ minor,
68
+ patch,
69
+ stage: "",
70
+ stageVersion: -1
71
+ };
72
+ if (match[4]) {
73
+ let [stage, _v] = match[4].split(".");
74
+ ret.stage = stage;
75
+ ret.stageVersion = Number(_v) || -1;
76
+ }
77
+ if (Number.isNaN(major) || Number.isNaN(minor) || Number.isNaN(patch) || Number.isNaN(ret.stageVersion)) {
78
+ throw new TypeError(`invalid version: ${version}`);
79
+ }
80
+ return ret;
81
+ }
82
+ function isUpdateJSON(json) {
83
+ const is = (j) => !!(j && j.minimumVersion && j.signature && j.size && j.version);
84
+ return is(json) && is(json?.beta);
85
+ }
86
+
70
87
  export {
71
88
  __require,
72
- parseVersion,
73
89
  unzipFile,
74
- zipFile
90
+ zipFile,
91
+ parseGithubCdnURL,
92
+ handleUnexpectedErrors,
93
+ parseVersion,
94
+ isUpdateJSON
75
95
  };
@@ -30,14 +30,7 @@ var verify = (buffer, signature2, cert) => {
30
30
  }
31
31
  };
32
32
 
33
- // src/updateJson.ts
34
- function isUpdateJSON(json) {
35
- const is = (j) => "signature" in j && "version" in j && "size" in j && "minimumVersion" in j;
36
- return is(json) && "beta" in json && is(json.beta);
37
- }
38
-
39
33
  export {
40
34
  signature,
41
- verify,
42
- isUpdateJSON
35
+ verify
43
36
  };
@@ -0,0 +1,148 @@
1
+ import {
2
+ __require
3
+ } from "./chunk-GB6VLKJZ.mjs";
4
+
5
+ // src/utils/electron.ts
6
+ import { existsSync, mkdirSync, readFileSync } from "node:fs";
7
+ import { basename, dirname, join } from "node:path";
8
+ import { release } from "node:os";
9
+ import { app } from "electron";
10
+ var is = {
11
+ dev: !app.isPackaged,
12
+ win: process.platform === "win32",
13
+ mac: process.platform === "darwin",
14
+ linux: process.platform === "linux"
15
+ };
16
+ function getPathFromAppNameAsar(...path) {
17
+ return is.dev ? "DEV.asar" : join(dirname(app.getAppPath()), `${app.name}.asar`, ...path);
18
+ }
19
+ function getVersions() {
20
+ const platform = is.win ? "Windows" : is.mac ? "MacOS" : process.platform.toUpperCase();
21
+ return {
22
+ appVersion: is.dev ? app.getVersion() : readFileSync(getPathFromAppNameAsar("version"), "utf-8"),
23
+ entryVersion: app.getVersion(),
24
+ electronVersion: process.versions.electron,
25
+ nodeVersion: process.versions.node,
26
+ systemVersion: `${platform} ${release()}`
27
+ };
28
+ }
29
+ function loadNativeModuleFromEntry(devEntryDirPath = "../../dist-entry", entryDirPath = join(app.getAppPath(), basename(devEntryDirPath))) {
30
+ const path = is.dev ? devEntryDirPath : entryDirPath;
31
+ return (moduleName) => {
32
+ try {
33
+ return __require(join(path, moduleName));
34
+ } catch (error) {
35
+ console.error("fail to load module", error);
36
+ }
37
+ };
38
+ }
39
+ function restartApp() {
40
+ app.relaunch();
41
+ app.quit();
42
+ }
43
+ function setAppUserModelId(id) {
44
+ app.setAppUserModelId(is.dev ? process.execPath : id ?? `org.${app.name}`);
45
+ }
46
+ function disableHWAccForWin7() {
47
+ if (release().startsWith("6.1")) {
48
+ app.disableHardwareAcceleration();
49
+ }
50
+ }
51
+ function singleInstance(window) {
52
+ const result = app.requestSingleInstanceLock();
53
+ result ? app.on("second-instance", () => {
54
+ if (window) {
55
+ window.show();
56
+ if (window.isMinimized()) {
57
+ window.restore();
58
+ }
59
+ window.focus();
60
+ }
61
+ }) : app.quit();
62
+ return result;
63
+ }
64
+ function setPortableAppDataPath(dirName = "data") {
65
+ const portablePath = join(dirname(app.getPath("exe")), dirName);
66
+ if (!existsSync(portablePath)) {
67
+ mkdirSync(portablePath);
68
+ }
69
+ app.setPath("appData", portablePath);
70
+ }
71
+ function waitAppReady(timeout = 1e3) {
72
+ return app.isReady() ? Promise.resolve() : new Promise((resolve, reject) => {
73
+ const _ = setTimeout(() => {
74
+ reject(new Error("app is not ready"));
75
+ }, timeout);
76
+ app.whenReady().then(() => {
77
+ clearTimeout(_);
78
+ resolve();
79
+ });
80
+ });
81
+ }
82
+ function getPaths(entryDirName = "dist-entry") {
83
+ const root = join(__dirname, "..");
84
+ const mainDirPath = join(root, "main");
85
+ const preloadDirPath = join(root, "preload");
86
+ const rendererDirPath = join(root, "renderer");
87
+ const devServerURL = process.env.VITE_DEV_SERVER_URL;
88
+ const indexHTMLPath = join(rendererDirPath, "index.html");
89
+ const publicDirPath = devServerURL ? join(root, "../public") : rendererDirPath;
90
+ return {
91
+ /**
92
+ * @example
93
+ * ```ts
94
+ * devServerURL && win.loadURL(devServerURL)
95
+ * ```
96
+ */
97
+ devServerURL,
98
+ /**
99
+ * @example
100
+ * ```ts
101
+ * win.loadFile(indexHTMLPath)
102
+ * ```
103
+ */
104
+ indexHTMLPath,
105
+ /**
106
+ * get path inside entry asar
107
+ * @param paths joined path
108
+ */
109
+ getPathFromEntryAsar(...paths) {
110
+ return join(app.getAppPath(), entryDirName, ...paths);
111
+ },
112
+ /**
113
+ * get path inside `${app.name}.asar/main`
114
+ * @param paths joined path
115
+ */
116
+ getPathFromMain(...paths) {
117
+ return join(mainDirPath, ...paths);
118
+ },
119
+ /**
120
+ * get path inside `${app.name}.asar/preload`
121
+ * @param paths joined path
122
+ */
123
+ getPathFromPreload(...paths) {
124
+ return join(preloadDirPath, ...paths);
125
+ },
126
+ /**
127
+ * get path inside public dir
128
+ * @param paths joined path
129
+ */
130
+ getPathFromPublic(...paths) {
131
+ return join(publicDirPath, ...paths);
132
+ }
133
+ };
134
+ }
135
+
136
+ export {
137
+ is,
138
+ getPathFromAppNameAsar,
139
+ getVersions,
140
+ loadNativeModuleFromEntry,
141
+ restartApp,
142
+ setAppUserModelId,
143
+ disableHWAccForWin7,
144
+ singleInstance,
145
+ setPortableAppDataPath,
146
+ waitAppReady,
147
+ getPaths
148
+ };