electron-incremental-update 3.0.0-beta.4 → 3.0.0-beta.6
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 +580 -887
- package/dist/{zip-Dwm7s1C9.mjs → download-BGaAyi1Z.mjs} +16 -69
- package/dist/download-BYnkme_X.cjs +167 -0
- package/dist/{download-BN4uMS4_.d.mts → download-BjWmHHAu.d.cts} +1 -1
- package/dist/{download-DO7iuxEJ.d.cts → download-DVWJfV3S.d.mts} +1 -1
- package/dist/{electron-BJCk7uxG.mjs → electron-BInvFJ-W.mjs} +38 -13
- package/dist/electron-D_8AbLQ5.cjs +346 -0
- package/dist/index.cjs +64 -32
- package/dist/index.d.cts +10 -13
- package/dist/index.d.mts +10 -13
- package/dist/index.mjs +60 -29
- package/dist/local-Daf8naRn.cjs +118 -0
- package/dist/local-s1cw_vwb.mjs +105 -0
- package/dist/provider.cjs +21 -40
- package/dist/provider.d.cts +53 -9
- package/dist/provider.d.mts +53 -9
- package/dist/provider.mjs +9 -30
- package/dist/{types-BM9Jfu7q.d.cts → types-BOqQ_r5Q.d.cts} +5 -5
- package/dist/{types-DASqEPXE.d.mts → types-BOqQ_r5Q.d.mts} +5 -5
- package/dist/utils.cjs +15 -15
- package/dist/utils.d.cts +2 -2
- package/dist/utils.d.mts +2 -2
- package/dist/utils.mjs +4 -5
- package/dist/vite.d.mts +122 -167
- package/dist/vite.mjs +684 -753
- package/dist/zip-D1dbBzw4.cjs +254 -0
- package/dist/zip-DUK3opmV.mjs +159 -0
- package/package.json +30 -35
- package/dist/electron-C-qmVhAt.cjs +0 -321
- package/dist/version--eVB2A7n.mjs +0 -72
- package/dist/version-aPrLuz_-.cjs +0 -129
- package/dist/zip-BCC7FAQ_.cjs +0 -264
package/dist/vite.mjs
CHANGED
|
@@ -1,24 +1,74 @@
|
|
|
1
|
-
import { builtinModules
|
|
2
|
-
import * as babel from "@babel/core";
|
|
3
|
-
import { getPackageInfoSync, loadPackageJSON } from "local-pkg";
|
|
4
|
-
import MagicString from "magic-string";
|
|
5
|
-
import cp from "node:child_process";
|
|
1
|
+
import { builtinModules } from "node:module";
|
|
6
2
|
import fs from "node:fs";
|
|
7
3
|
import path from "node:path";
|
|
8
|
-
import { build, createFilter, createLogger, mergeConfig, normalizePath, version } from "vite";
|
|
9
4
|
import { isCI } from "ci-info";
|
|
10
|
-
import {
|
|
5
|
+
import { createLogger, mergeConfig, normalizePath } from "vite";
|
|
6
|
+
import { electronPluginFactory } from "vite-plugin-electron/multi-env";
|
|
7
|
+
import { esmShim } from "vite-plugin-electron/plugin";
|
|
11
8
|
import crypto from "node:crypto";
|
|
12
9
|
import zlib from "node:zlib";
|
|
10
|
+
import cp from "node:child_process";
|
|
11
|
+
import * as babel from "@babel/core";
|
|
12
|
+
import { copyFile, cp as cp$1, mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
|
13
|
+
import { tmpdir } from "node:os";
|
|
14
|
+
import { createPackage, extractFile } from "@electron/asar";
|
|
13
15
|
import { generate } from "selfsigned";
|
|
14
|
-
|
|
16
|
+
//#region src/vite/startup.ts
|
|
17
|
+
/**
|
|
18
|
+
* Filter error messages from stdout/stderr during startup
|
|
19
|
+
* @param args - Startup arguments
|
|
20
|
+
* @param filter - Filter function to determine which messages to show
|
|
21
|
+
*/
|
|
22
|
+
async function filterErrorMessageStartup(filter) {
|
|
23
|
+
const elec = process.electronApp;
|
|
24
|
+
elec.stdout?.addListener("data", (data) => {
|
|
25
|
+
console.log(data.toString().trimEnd());
|
|
26
|
+
});
|
|
27
|
+
elec.stderr?.addListener("data", (data) => {
|
|
28
|
+
const message = data.toString();
|
|
29
|
+
if (filter(message)) console.error(message);
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Fix Windows character encoding by setting code page to UTF-8
|
|
34
|
+
* @param fn - Function to wrap with encoding fix
|
|
35
|
+
* @returns Wrapped function with Windows encoding fix
|
|
36
|
+
*/
|
|
37
|
+
function fixWinCharEncoding(fn) {
|
|
38
|
+
return (async (...args) => {
|
|
39
|
+
if (process.platform === "win32") (await import("node:child_process")).spawnSync("chcp", ["65001"]);
|
|
40
|
+
await fn(...args);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
//#endregion
|
|
44
|
+
//#region src/utils/crypto.ts
|
|
45
|
+
function hashBuffer(data, length) {
|
|
46
|
+
const hash = crypto.createHash("SHA256").update(data).digest("binary");
|
|
47
|
+
return Buffer.from(hash).subarray(0, length);
|
|
48
|
+
}
|
|
49
|
+
function aesEncrypt(plainText, key, iv) {
|
|
50
|
+
const cipher = crypto.createCipheriv("aes-256-cbc", key, iv);
|
|
51
|
+
return cipher.update(plainText, "utf8", "base64url") + cipher.final("base64url");
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Default function to generate asar signature, returns generated signature
|
|
55
|
+
* @param buffer file buffer
|
|
56
|
+
* @param privateKey primary key
|
|
57
|
+
* @param cert certificate
|
|
58
|
+
* @param version target version
|
|
59
|
+
*/
|
|
60
|
+
function defaultSignature(buffer, privateKey, cert, version) {
|
|
61
|
+
return aesEncrypt(`${crypto.createSign("RSA-SHA256").update(buffer).sign(crypto.createPrivateKey(privateKey), "base64")}%${version}`, hashBuffer(cert, 32), hashBuffer(buffer, 16));
|
|
62
|
+
}
|
|
63
|
+
//#endregion
|
|
15
64
|
//#region src/utils/version.ts
|
|
65
|
+
const REG_VERSION = /^(\d+)\.(\d+)\.(\d+)(?:-([a-z0-9]+)(?:\.(\d+))?)?$/i;
|
|
16
66
|
/**
|
|
17
67
|
* Parse version string to {@link Version}, like `0.2.0-beta.1`
|
|
18
68
|
* @param version version string
|
|
19
69
|
*/
|
|
20
70
|
function parseVersion(version) {
|
|
21
|
-
const match =
|
|
71
|
+
const match = REG_VERSION.exec(version);
|
|
22
72
|
if (!match) throw new TypeError(`invalid version: ${version}`);
|
|
23
73
|
const [major, minor, patch] = match.slice(1, 4).map(Number);
|
|
24
74
|
const ret = {
|
|
@@ -29,9 +79,8 @@ function parseVersion(version) {
|
|
|
29
79
|
stageVersion: -1
|
|
30
80
|
};
|
|
31
81
|
if (match[4]) {
|
|
32
|
-
|
|
33
|
-
ret.
|
|
34
|
-
ret.stageVersion = Number(_v) || -1;
|
|
82
|
+
ret.stage = match[4];
|
|
83
|
+
ret.stageVersion = match[5] === void 0 ? -1 : Number(match[5]);
|
|
35
84
|
}
|
|
36
85
|
if (Number.isNaN(major) || Number.isNaN(minor) || Number.isNaN(patch) || Number.isNaN(ret.stageVersion)) throw new TypeError(`Invalid version: ${version}`);
|
|
37
86
|
return ret;
|
|
@@ -42,7 +91,7 @@ const is = (j) => !!(j && j.minimumVersion && j.signature && j.version);
|
|
|
42
91
|
* @param json any variable
|
|
43
92
|
*/
|
|
44
93
|
function isUpdateJSON(json) {
|
|
45
|
-
return is(json) && is(json
|
|
94
|
+
return json && is(json) && is(json.beta);
|
|
46
95
|
}
|
|
47
96
|
/**
|
|
48
97
|
* Default function to generate `UpdateJSON`
|
|
@@ -64,141 +113,424 @@ function defaultVersionJsonGenerator(existingJson, signature, version, minimumVe
|
|
|
64
113
|
}
|
|
65
114
|
return existingJson;
|
|
66
115
|
}
|
|
67
|
-
|
|
116
|
+
//#endregion
|
|
117
|
+
//#region src/utils/zip.ts
|
|
118
|
+
/**
|
|
119
|
+
* Default function to compress file using brotli
|
|
120
|
+
* @param buffer uncompressed file buffer
|
|
121
|
+
*/
|
|
122
|
+
async function defaultZipFile(buffer) {
|
|
123
|
+
return new Promise((resolve, reject) => {
|
|
124
|
+
zlib.brotliCompress(buffer, (err, buffer) => err ? reject(err) : resolve(buffer));
|
|
125
|
+
});
|
|
126
|
+
}
|
|
68
127
|
//#endregion
|
|
69
128
|
//#region src/vite/constant.ts
|
|
70
|
-
const id = "electron-incremental-
|
|
129
|
+
const id = "electron-incremental-update";
|
|
71
130
|
const bytecodeId = `${id}-bytecode`;
|
|
72
131
|
const log = createLogger("info", { prefix: `[${id}]` });
|
|
73
132
|
const bytecodeLog = createLogger("info", { prefix: `[${bytecodeId}]` });
|
|
74
|
-
|
|
133
|
+
const defaultExternal = [
|
|
134
|
+
...builtinModules,
|
|
135
|
+
"electron",
|
|
136
|
+
"electron/common",
|
|
137
|
+
"electron/main",
|
|
138
|
+
"electron/renderer",
|
|
139
|
+
"electron/utility",
|
|
140
|
+
/^node:/,
|
|
141
|
+
/.*\.(node|dll|dylib|so)$/,
|
|
142
|
+
"original-fs"
|
|
143
|
+
];
|
|
75
144
|
//#endregion
|
|
76
145
|
//#region src/vite/bytecode/code.ts
|
|
77
146
|
const bytecodeGeneratorScript = "const vm = require('vm')\nconst v8 = require('v8')\nconst wrap = require('module').wrap\nv8.setFlagsFromString('--no-lazy')\nv8.setFlagsFromString('--no-flush-bytecode')\nlet code = ''\nprocess.stdin.setEncoding('utf-8')\nprocess.stdin.on('readable', () => {\n const data = process.stdin.read()\n if (data !== null) {\n code += data\n }\n})\nprocess.stdin.on('end', () => {\n try {\n if (typeof code !== 'string') {\n throw new Error('javascript code must be string.')\n }\n const script = new vm.Script(wrap(code), { produceCachedData: true })\n const bytecodeBuffer = script.createCachedData()\n process.stdout.write(bytecodeBuffer)\n } catch (error) {\n console.error(error)\n }\n})\n";
|
|
78
147
|
const bytecodeModuleLoaderCode = "\"use strict\";\nconst fs = require(\"fs\");\nconst path = require(\"path\");\nconst vm = require(\"vm\");\nconst v8 = require(\"v8\");\nconst Module = require(\"module\");\nv8.setFlagsFromString(\"--no-lazy\");\nv8.setFlagsFromString(\"--no-flush-bytecode\");\nconst FLAG_HASH_OFFSET = 12;\nconst SOURCE_HASH_OFFSET = 8;\nlet dummyBytecode;\nfunction setFlagHashHeader(bytecodeBuffer) {\n if (!dummyBytecode) {\n const script = new vm.Script(\"\", {\n produceCachedData: true\n });\n dummyBytecode = script.createCachedData();\n }\n dummyBytecode.slice(FLAG_HASH_OFFSET, FLAG_HASH_OFFSET + 4).copy(bytecodeBuffer, FLAG_HASH_OFFSET);\n};\nfunction getSourceHashHeader(bytecodeBuffer) {\n return bytecodeBuffer.slice(SOURCE_HASH_OFFSET, SOURCE_HASH_OFFSET + 4);\n};\nfunction buffer2Number(buffer) {\n let ret = 0;\n ret |= buffer[3] << 24;\n ret |= buffer[2] << 16;\n ret |= buffer[1] << 8;\n ret |= buffer[0];\n return ret;\n};\nModule._extensions[\".jsc\"] = Module._extensions[\".cjsc\"] = function (module, filename) {\n const bytecodeBuffer = fs.readFileSync(filename);\n if (!Buffer.isBuffer(bytecodeBuffer)) {\n throw new Error(\"BytecodeBuffer must be a buffer object.\");\n }\n setFlagHashHeader(bytecodeBuffer);\n const length = buffer2Number(getSourceHashHeader(bytecodeBuffer));\n let dummyCode = \"\";\n if (length > 1) {\n dummyCode = \"\\\"\" + \"\\u200b\".repeat(length - 2) + \"\\\"\";\n }\n const script = new vm.Script(dummyCode, {\n filename: filename,\n lineOffset: 0,\n displayErrors: true,\n cachedData: bytecodeBuffer\n });\n if (script.cachedDataRejected) {\n throw new Error(\"Invalid or incompatible cached data (cachedDataRejected)\");\n }\n const require = function (id) {\n return module.require(id);\n };\n require.resolve = function (request, options) {\n return Module._resolveFilename(request, module, false, options);\n };\n if (process.mainModule) {\n require.main = process.mainModule;\n }\n require.extensions = Module._extensions;\n require.cache = Module._cache;\n const compiledWrapper = script.runInThisContext({\n filename: filename,\n lineOffset: 0,\n columnOffset: 0,\n displayErrors: true\n });\n const dirname = path.dirname(filename);\n const args = [module.exports, require, module, filename, dirname, process, global];\n return compiledWrapper.apply(module.exports, args);\n};\n";
|
|
79
|
-
|
|
80
148
|
//#endregion
|
|
81
149
|
//#region src/vite/bytecode/utils.ts
|
|
82
|
-
const electronModule = getPackageInfoSync("electron");
|
|
83
|
-
const electronMajorVersion = parseVersion(electronModule.version).major;
|
|
84
150
|
const useStrict = "'use strict';";
|
|
85
151
|
const bytecodeModuleLoader = "__loader__.js";
|
|
86
|
-
function
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
if (!
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
152
|
+
async function resolvePaths(customPath) {
|
|
153
|
+
if (!customPath || !process.CACHED_ELECTRON_PATH) process.CACHED_ELECTRON_PATH = (await import("electron")).default;
|
|
154
|
+
if (!process.CACHED_BYTECODE_COMPILER_PATH) process.CACHED_BYTECODE_COMPILER_PATH = path.join(path.dirname(process.CACHED_ELECTRON_PATH), "EIU_bytenode.cjs");
|
|
155
|
+
if (!fs.existsSync(process.CACHED_BYTECODE_COMPILER_PATH)) fs.writeFileSync(process.CACHED_BYTECODE_COMPILER_PATH, bytecodeGeneratorScript);
|
|
156
|
+
return {
|
|
157
|
+
electronPath: customPath || process.CACHED_ELECTRON_PATH,
|
|
158
|
+
bytecodePath: process.CACHED_BYTECODE_COMPILER_PATH
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
async function compileToBytecode(code, name, customElectronPath) {
|
|
162
|
+
try {
|
|
163
|
+
const { bytecodePath, electronPath } = await resolvePaths(customElectronPath);
|
|
164
|
+
return await new Promise((resolve, reject) => {
|
|
165
|
+
const proc = cp.spawn(electronPath, [bytecodePath], {
|
|
166
|
+
env: {
|
|
167
|
+
...process.env,
|
|
168
|
+
ELECTRON_RUN_AS_NODE: "1"
|
|
169
|
+
},
|
|
170
|
+
stdio: [
|
|
171
|
+
"pipe",
|
|
172
|
+
"pipe",
|
|
173
|
+
"pipe",
|
|
174
|
+
"ipc"
|
|
175
|
+
]
|
|
176
|
+
});
|
|
177
|
+
const stdoutChunks = [];
|
|
178
|
+
const stderrChunks = [];
|
|
179
|
+
if (proc.stdin) {
|
|
180
|
+
proc.stdin.write(code);
|
|
181
|
+
proc.stdin.end();
|
|
182
|
+
}
|
|
183
|
+
if (proc.stdout) proc.stdout.on("data", (chunk_2) => stdoutChunks.push(chunk_2)).on("error", (err) => reject(err));
|
|
184
|
+
if (proc.stderr) proc.stderr.on("data", (chunk_3) => stderrChunks.push(chunk_3)).on("error", (err_1) => reject(err_1));
|
|
185
|
+
proc.on("error", (err_2) => reject(err_2));
|
|
186
|
+
proc.on("close", (exitCode) => {
|
|
187
|
+
const stdout = Buffer.concat(stdoutChunks);
|
|
188
|
+
const errorMessage = Buffer.concat(stderrChunks).toString("utf-8");
|
|
189
|
+
if (exitCode !== 0 || stdout.length === 0) {
|
|
190
|
+
reject(/* @__PURE__ */ new Error(`Bytecode generation process exited with code ${exitCode}. Error: ${errorMessage}`));
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
resolve(stdout);
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
} catch (e) {
|
|
197
|
+
return `Failed to generate bytecode of [${name}], ${e}`;
|
|
98
198
|
}
|
|
99
|
-
return electronExecPath;
|
|
100
199
|
}
|
|
101
|
-
function
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
200
|
+
const decodeFnBody = babel.parse(";function _0xstr_(a,b){return String.fromCharCode.apply(0,a.map(function(x){return x-b}))};")?.program.body ?? [];
|
|
201
|
+
function createObfuscatedStringCall(input, offset) {
|
|
202
|
+
const resolvedOffset = offset ?? ~~(Math.random() * 16) + 1;
|
|
203
|
+
const elements = input.split("").map((char) => {
|
|
204
|
+
const value = char.codePointAt(0) + resolvedOffset;
|
|
205
|
+
const node = babel.types.numericLiteral(value);
|
|
206
|
+
node.extra = {
|
|
207
|
+
raw: `0x${value.toString(16)}`,
|
|
208
|
+
rawValue: value
|
|
209
|
+
};
|
|
210
|
+
return node;
|
|
211
|
+
});
|
|
212
|
+
return babel.types.callExpression(babel.types.identifier("_0xstr_"), [babel.types.arrayExpression(elements), babel.types.numericLiteral(resolvedOffset)]);
|
|
213
|
+
}
|
|
214
|
+
function createPrepareContext(bytecodeFileNames) {
|
|
215
|
+
const requireRewrites = {};
|
|
216
|
+
for (const fileName of bytecodeFileNames) {
|
|
217
|
+
if (!fileName.endsWith(".js") && !fileName.endsWith(".cjs")) continue;
|
|
218
|
+
const baseName = path.posix.basename(fileName);
|
|
219
|
+
requireRewrites[baseName] = `${baseName}c`;
|
|
220
|
+
}
|
|
221
|
+
return {
|
|
222
|
+
requireRewrites,
|
|
223
|
+
hasRequireRewrites: Object.keys(requireRewrites).length > 0
|
|
224
|
+
};
|
|
105
225
|
}
|
|
106
|
-
function
|
|
107
|
-
const
|
|
108
|
-
|
|
226
|
+
function rewriteRequirePath(requirePath, requireRewrites) {
|
|
227
|
+
const baseName = path.posix.basename(requirePath);
|
|
228
|
+
const newBaseName = requireRewrites[baseName];
|
|
229
|
+
if (!newBaseName) return;
|
|
230
|
+
return `${requirePath.slice(0, -baseName.length)}${newBaseName}`;
|
|
231
|
+
}
|
|
232
|
+
function rewriteSimpleRequireCalls(code, requireRewrites) {
|
|
233
|
+
if (!code.includes("require(")) return code;
|
|
234
|
+
return code.replace(/\brequire\(\s*(['"])([^'"]+)\1\s*\)/g, (match, quote, requirePath) => {
|
|
235
|
+
const newRequirePath = rewriteRequirePath(requirePath, requireRewrites);
|
|
236
|
+
return newRequirePath ? `require(${quote}${newRequirePath}${quote})` : match;
|
|
237
|
+
});
|
|
109
238
|
}
|
|
110
|
-
|
|
111
|
-
function
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
239
|
+
function obfuscateStringsPlugin(_, options) {
|
|
240
|
+
function transformProperty(path) {
|
|
241
|
+
const node = path.node;
|
|
242
|
+
if (node.computed || !babel.types.isIdentifier(node.property)) return;
|
|
243
|
+
if (path.findParent((parent) => parent.isFunctionDeclaration() && babel.types.isIdentifier(parent.node.id, { name: "_0xstr_" }))) return;
|
|
244
|
+
node.computed = true;
|
|
245
|
+
node.property = babel.types.stringLiteral(node.property.name);
|
|
246
|
+
}
|
|
247
|
+
return { visitor: {
|
|
248
|
+
MemberExpression(path) {
|
|
249
|
+
transformProperty(path);
|
|
250
|
+
},
|
|
251
|
+
OptionalMemberExpression(path) {
|
|
252
|
+
transformProperty(path);
|
|
253
|
+
},
|
|
254
|
+
ObjectProperty(path, state) {
|
|
255
|
+
const key = path.node.key;
|
|
256
|
+
if (path.node.computed || path.node.shorthand || !babel.types.isIdentifier(key) || key.name === "__proto__") return;
|
|
257
|
+
path.node.computed = true;
|
|
258
|
+
path.node.key = createObfuscatedStringCall(key.name, options.offset);
|
|
259
|
+
state.hasTransformed = true;
|
|
260
|
+
},
|
|
261
|
+
StringLiteral(path, state) {
|
|
262
|
+
const parent = path.parent;
|
|
263
|
+
const node = path.node;
|
|
264
|
+
if (parent.type === "CallExpression") {
|
|
265
|
+
if (parent.callee.type === "Identifier" && parent.callee.name === "require") return;
|
|
266
|
+
if (parent.callee.type === "Import") return;
|
|
267
|
+
}
|
|
268
|
+
if (parent.type.startsWith("Export")) return;
|
|
269
|
+
if (parent.type.startsWith("Import")) return;
|
|
270
|
+
if (parent.type === "ObjectMethod" && parent.key === node) {
|
|
271
|
+
parent.computed = true;
|
|
272
|
+
path.replaceWith(createObfuscatedStringCall(node.value, options.offset));
|
|
273
|
+
state.hasTransformed = true;
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
if (parent.type === "ObjectProperty" && parent.key === node) {
|
|
277
|
+
parent.computed = true;
|
|
278
|
+
path.replaceWith(createObfuscatedStringCall(node.value, options.offset));
|
|
279
|
+
state.hasTransformed = true;
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
if (!node.value.trim()) return;
|
|
283
|
+
path.replaceWith(createObfuscatedStringCall(node.value, options.offset));
|
|
284
|
+
state.hasTransformed = true;
|
|
285
|
+
},
|
|
286
|
+
Program: { exit(path, state) {
|
|
287
|
+
if (!state.hasTransformed) return;
|
|
288
|
+
path.unshiftContainer("body", decodeFnBody.map((node) => babel.types.cloneNode(node)));
|
|
289
|
+
} }
|
|
290
|
+
} };
|
|
291
|
+
}
|
|
292
|
+
function rewriteRequirePlugin(_, options) {
|
|
293
|
+
return { visitor: { CallExpression(path) {
|
|
294
|
+
if (!babel.types.isIdentifier(path.node.callee, { name: "require" }) || path.node.arguments.length === 0) return;
|
|
295
|
+
const arg = path.node.arguments[0];
|
|
296
|
+
if (!babel.types.isStringLiteral(arg)) return;
|
|
297
|
+
const newRequirePath = rewriteRequirePath(arg.value, options.requireRewrites);
|
|
298
|
+
if (newRequirePath) path.node.arguments[0] = babel.types.stringLiteral(newRequirePath);
|
|
299
|
+
} } };
|
|
300
|
+
}
|
|
301
|
+
function prepare(code, minify, context, offset) {
|
|
302
|
+
if (!code.includes("\"") && !code.includes("'") && !code.includes("`") && !code.includes("=>") && !/\.[A-Za-z_$]/.test(code) && !/[{,]\s*[A-Za-z_$][\w$]*\s*:/.test(code)) return { code };
|
|
303
|
+
if (context.hasRequireRewrites && !code.includes("`") && !code.includes("=>")) {
|
|
304
|
+
const codeWithoutSimpleRequires = code.replace(/\brequire\(\s*(['"])([^'"]+)\1\s*\)/g, "");
|
|
305
|
+
if (!codeWithoutSimpleRequires.includes("\"") && !codeWithoutSimpleRequires.includes("'")) return { code: rewriteSimpleRequireCalls(code, context.requireRewrites) };
|
|
306
|
+
}
|
|
307
|
+
return babel.transform(code, {
|
|
308
|
+
minified: minify,
|
|
309
|
+
plugins: [
|
|
310
|
+
"@babel/plugin-transform-arrow-functions",
|
|
311
|
+
"@babel/plugin-transform-template-literals",
|
|
312
|
+
[obfuscateStringsPlugin, { offset }],
|
|
313
|
+
[rewriteRequirePlugin, { requireRewrites: context.requireRewrites }]
|
|
314
|
+
]
|
|
140
315
|
});
|
|
141
316
|
}
|
|
142
|
-
|
|
143
|
-
|
|
317
|
+
//#endregion
|
|
318
|
+
//#region src/vite/bytecode/index.ts
|
|
319
|
+
function getBytecodeLoaderBlock(chunkFileName) {
|
|
320
|
+
const loaderFileName = path.posix.relative(path.posix.dirname(chunkFileName), bytecodeModuleLoader);
|
|
321
|
+
return `require("${loaderFileName.startsWith(".") ? loaderFileName : `./${loaderFileName}`}")`;
|
|
322
|
+
}
|
|
323
|
+
function bytecodePlugin(env, minify, isESM, options) {
|
|
324
|
+
const { enable, preload = false, electronPath, beforeCompile } = options;
|
|
325
|
+
if (!enable) return null;
|
|
326
|
+
if (env === "preload" && !preload) {
|
|
327
|
+
if (preload === void 0) bytecodeLog.warn("`bytecodePlugin` is skipped in preload. Set `bytecode: { preload: false }` to disable this warning, or `bytecode: { preload: true }` and set `sandbox: false` in BrowserWindow to enable bytecode in preload.", { timestamp: true });
|
|
328
|
+
return null;
|
|
329
|
+
}
|
|
330
|
+
if (isESM) throw new Error("`bytecodePlugin` requires CommonJS. Set \"type\": \"commonjs\" in package.json and use .cjs extensions");
|
|
331
|
+
let hasJsChunks = false;
|
|
144
332
|
return {
|
|
145
|
-
|
|
146
|
-
|
|
333
|
+
name: bytecodeId,
|
|
334
|
+
async generateBundle(outputOptions, bundle) {
|
|
335
|
+
hasJsChunks = Object.values(bundle).some((file) => file.type === "chunk" && (file.fileName.endsWith(".js") || file.fileName.endsWith(".cjs")));
|
|
336
|
+
if (hasJsChunks) this.emitFile({
|
|
337
|
+
type: "asset",
|
|
338
|
+
source: `${bytecodeModuleLoaderCode}\n`,
|
|
339
|
+
name: "Bytecode Loader",
|
|
340
|
+
fileName: bytecodeModuleLoader
|
|
341
|
+
});
|
|
342
|
+
if (!hasJsChunks) return;
|
|
343
|
+
const outputDir = outputOptions.dir ?? (outputOptions.file && path.dirname(outputOptions.file));
|
|
344
|
+
const prepareContext = createPrepareContext(Object.values(bundle).filter((f) => f.type === "chunk" && !f.isEntry).map((c) => path.posix.basename(c.fileName)));
|
|
345
|
+
await Promise.all(Object.entries(bundle).map(async ([fileName, item]) => {
|
|
346
|
+
if (item.type !== "chunk" || fileName === "__loader__.js") return;
|
|
347
|
+
const chunk = item;
|
|
348
|
+
const bytecodeFileName = `${fileName}c`;
|
|
349
|
+
const absPath = outputDir ? path.join(outputDir, fileName) : fileName;
|
|
350
|
+
let code = prepare(chunk.code, minify, prepareContext)?.code || chunk.code;
|
|
351
|
+
if (beforeCompile) {
|
|
352
|
+
const hookResult = await beforeCompile(code, absPath);
|
|
353
|
+
if (hookResult) code = hookResult;
|
|
354
|
+
}
|
|
355
|
+
const bytecode = await compileToBytecode(code, absPath, electronPath);
|
|
356
|
+
if (typeof bytecode === "string") throw new TypeError(bytecode);
|
|
357
|
+
this.emitFile({
|
|
358
|
+
type: "asset",
|
|
359
|
+
source: bytecode,
|
|
360
|
+
fileName: bytecodeFileName
|
|
361
|
+
});
|
|
362
|
+
if (chunk.isEntry) chunk.code = `${useStrict}\n${getBytecodeLoaderBlock(fileName)}\nmodule.exports=require("./${path.posix.basename(fileName)}c");\n`;
|
|
363
|
+
else delete bundle[fileName];
|
|
364
|
+
}));
|
|
365
|
+
}
|
|
147
366
|
};
|
|
148
367
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
368
|
+
//#endregion
|
|
369
|
+
//#region src/vite/local-dev-update.ts
|
|
370
|
+
const LOCAL_DEV_SIGNATURE = "local-dev";
|
|
371
|
+
function resolveLocalDevUpdateOptions(root, options) {
|
|
372
|
+
if (!options) return;
|
|
373
|
+
const resolvedOptions = options === true ? {} : options;
|
|
374
|
+
return {
|
|
375
|
+
baseDir: path.resolve(root, resolvedOptions.baseDir ?? "release/local-update"),
|
|
376
|
+
installedAsarPath: path.resolve(root, "DEV.asar"),
|
|
377
|
+
packageJsonPath: resolvedOptions.packageJsonPath ? path.resolve(root, resolvedOptions.packageJsonPath) : void 0,
|
|
378
|
+
chunkSize: resolvedOptions.chunkSize,
|
|
379
|
+
chunkDelay: resolvedOptions.chunkDelay
|
|
380
|
+
};
|
|
152
381
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
function
|
|
160
|
-
const
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
382
|
+
async function resolveLocalDevUpdatePackage(fallbackPkg, localDevUpdate) {
|
|
383
|
+
if (!localDevUpdate?.packageJsonPath) return fallbackPkg;
|
|
384
|
+
const pkg = JSON.parse(await readFile(localDevUpdate.packageJsonPath, "utf-8"));
|
|
385
|
+
if (!pkg.name || !pkg.version || !pkg.main) throw new Error("localDevUpdate.packageJsonPath must contain name, version and main fields");
|
|
386
|
+
return pkg;
|
|
387
|
+
}
|
|
388
|
+
function getNextPatchVersion(version) {
|
|
389
|
+
const parsed = parseVersion(version);
|
|
390
|
+
return `${parsed.major}.${parsed.minor}.${parsed.patch + 1}`;
|
|
391
|
+
}
|
|
392
|
+
function isValidVersion(version) {
|
|
393
|
+
try {
|
|
394
|
+
parseVersion(version);
|
|
395
|
+
return true;
|
|
396
|
+
} catch {
|
|
397
|
+
return false;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
function readValidAsarVersion(asarPath) {
|
|
401
|
+
try {
|
|
402
|
+
const version = extractFile(asarPath, "version").toString("utf-8").trim();
|
|
403
|
+
return isValidVersion(version) ? version : void 0;
|
|
404
|
+
} catch {
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
async function installPendingAsar(installedAsarPath) {
|
|
409
|
+
const pendingAsarPath = `${installedAsarPath}.tmp`;
|
|
410
|
+
const version = readValidAsarVersion(pendingAsarPath);
|
|
411
|
+
if (!version) {
|
|
412
|
+
await rm(pendingAsarPath, { force: true });
|
|
413
|
+
return false;
|
|
414
|
+
}
|
|
415
|
+
try {
|
|
416
|
+
await copyFile(pendingAsarPath, installedAsarPath);
|
|
417
|
+
await rm(pendingAsarPath, { force: true });
|
|
418
|
+
log.info(`Installed pending local dev update ${version}`, { timestamp: true });
|
|
419
|
+
return true;
|
|
420
|
+
} catch (error) {
|
|
421
|
+
if (error.code === "ENOENT") return false;
|
|
422
|
+
throw error;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
async function readExistingUpdateJSON(versionPath, version) {
|
|
426
|
+
try {
|
|
427
|
+
const json = JSON.parse(await readFile(versionPath, "utf-8"));
|
|
428
|
+
if (isUpdateJSON(json)) return json;
|
|
429
|
+
} catch {}
|
|
430
|
+
return {
|
|
431
|
+
version,
|
|
432
|
+
minimumVersion: "0.0.0",
|
|
433
|
+
signature: LOCAL_DEV_SIGNATURE,
|
|
434
|
+
beta: {
|
|
435
|
+
version,
|
|
436
|
+
minimumVersion: "0.0.0",
|
|
437
|
+
signature: LOCAL_DEV_SIGNATURE
|
|
170
438
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
async function prepareLocalDevUpdateResource({ root, pkg, buildAsarOption, versionPath, minimumVersion, keepInstalledVersion, localDevUpdate }) {
|
|
442
|
+
const installedPendingUpdate = await installPendingAsar(localDevUpdate.installedAsarPath);
|
|
443
|
+
const installedVersion = readValidAsarVersion(localDevUpdate.installedAsarPath) ?? pkg.version;
|
|
444
|
+
const targetVersion = keepInstalledVersion && installedPendingUpdate ? installedVersion : getNextPatchVersion(installedVersion);
|
|
445
|
+
const workDir = await mkdtemp(path.join(tmpdir(), "eiu-local-dev-update-"));
|
|
446
|
+
const stagedElectronDistPath = path.join(workDir, "dist-electron");
|
|
447
|
+
const resolvedVersionPath = path.join(localDevUpdate.baseDir, versionPath);
|
|
448
|
+
const asarPath = path.join(localDevUpdate.baseDir, `${pkg.name}.asar`);
|
|
449
|
+
const gzipPath = path.join(localDevUpdate.baseDir, `${pkg.name}-${targetVersion}.asar.gz`);
|
|
450
|
+
try {
|
|
451
|
+
await mkdir(localDevUpdate.baseDir, { recursive: true });
|
|
452
|
+
await mkdir(path.dirname(resolvedVersionPath), { recursive: true });
|
|
453
|
+
await cp$1(path.resolve(root, buildAsarOption.electronDistPath), stagedElectronDistPath, { recursive: true });
|
|
454
|
+
await writeFile(path.join(stagedElectronDistPath, "version"), targetVersion, "utf-8");
|
|
455
|
+
await createPackage(stagedElectronDistPath, asarPath);
|
|
456
|
+
await writeFile(gzipPath, await buildAsarOption.generateGzipFile(await readFile(asarPath)));
|
|
457
|
+
const updateJSON = defaultVersionJsonGenerator(await readExistingUpdateJSON(resolvedVersionPath, targetVersion), LOCAL_DEV_SIGNATURE, targetVersion, minimumVersion);
|
|
458
|
+
if (!isUpdateJSON(updateJSON)) throw new Error("Invalid local dev update json");
|
|
459
|
+
await writeFile(resolvedVersionPath, JSON.stringify(updateJSON, null, 2), "utf-8");
|
|
460
|
+
log.info(`Prepared local dev update ${targetVersion}`, { timestamp: true });
|
|
461
|
+
return targetVersion;
|
|
462
|
+
} finally {
|
|
463
|
+
await rm(workDir, {
|
|
464
|
+
recursive: true,
|
|
465
|
+
force: true
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
function createLocalDevUpdateOnstart(args) {
|
|
470
|
+
let restartRequested = false;
|
|
471
|
+
let startupArgs;
|
|
472
|
+
let managedElectronApp;
|
|
473
|
+
function createStartupArgs(onstartArgs) {
|
|
474
|
+
return {
|
|
475
|
+
...onstartArgs,
|
|
476
|
+
startup(argv, options, customElectronPkg) {
|
|
477
|
+
const env = {
|
|
478
|
+
...process.env,
|
|
479
|
+
...options?.env
|
|
480
|
+
};
|
|
481
|
+
delete env.ELECTRON_RUN_AS_NODE;
|
|
482
|
+
return onstartArgs.startup(argv, {
|
|
483
|
+
...options,
|
|
484
|
+
env
|
|
485
|
+
}, customElectronPkg);
|
|
181
486
|
}
|
|
182
|
-
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
async function start(onstartArgs, options = {}) {
|
|
490
|
+
const safeStartupArgs = createStartupArgs(onstartArgs);
|
|
491
|
+
startupArgs = safeStartupArgs;
|
|
492
|
+
await prepareLocalDevUpdateResource({
|
|
493
|
+
...args,
|
|
494
|
+
keepInstalledVersion: options.keepInstalledVersion
|
|
495
|
+
});
|
|
496
|
+
if (args.userOnstart) await args.userOnstart(safeStartupArgs);
|
|
497
|
+
else await safeStartupArgs.startup();
|
|
498
|
+
const electronApp = process.electronApp;
|
|
499
|
+
removeManagedListeners();
|
|
500
|
+
managedElectronApp = electronApp;
|
|
501
|
+
managedElectronApp?.removeListener("exit", process.exit);
|
|
502
|
+
managedElectronApp?.on("message", handleMessage);
|
|
503
|
+
managedElectronApp?.once("exit", handleExit);
|
|
504
|
+
}
|
|
505
|
+
function handleMessage(message) {
|
|
506
|
+
if (message === "eiu:restart") {
|
|
507
|
+
restartRequested = true;
|
|
508
|
+
managedElectronApp?.send?.("eiu:restart-ready");
|
|
183
509
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
510
|
+
}
|
|
511
|
+
function handleExit() {
|
|
512
|
+
if (restartRequested) {
|
|
513
|
+
restartRequested = false;
|
|
514
|
+
setTimeout(() => {
|
|
515
|
+
if (startupArgs) start(startupArgs, { keepInstalledVersion: true });
|
|
516
|
+
});
|
|
517
|
+
return;
|
|
191
518
|
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
}
|
|
519
|
+
process.exit();
|
|
520
|
+
}
|
|
521
|
+
function removeManagedListeners() {
|
|
522
|
+
managedElectronApp?.removeListener("message", handleMessage);
|
|
523
|
+
managedElectronApp?.removeListener("exit", handleExit);
|
|
524
|
+
}
|
|
525
|
+
return (onstartArgs) => start(onstartArgs);
|
|
198
526
|
}
|
|
199
|
-
|
|
200
527
|
//#endregion
|
|
201
|
-
//#region src/vite/utils.ts
|
|
528
|
+
//#region src/vite/utils/file.ts
|
|
529
|
+
/**
|
|
530
|
+
* Convert byte size to human-readable format
|
|
531
|
+
* @param size - Size in bytes
|
|
532
|
+
* @returns Human-readable size string (e.g., "1.23 MB")
|
|
533
|
+
*/
|
|
202
534
|
function readableSize(size) {
|
|
203
535
|
const units = [
|
|
204
536
|
"B",
|
|
@@ -213,6 +545,12 @@ function readableSize(size) {
|
|
|
213
545
|
}
|
|
214
546
|
return `${size.toFixed(2)} ${units[i]}`;
|
|
215
547
|
}
|
|
548
|
+
/**
|
|
549
|
+
* Copy file/directory, skipping if target exists
|
|
550
|
+
* @param from - Source path
|
|
551
|
+
* @param to - Destination path
|
|
552
|
+
* @param skipIfExist - Skip copy if destination exists
|
|
553
|
+
*/
|
|
216
554
|
function copyAndSkipIfExist(from, to, skipIfExist) {
|
|
217
555
|
if (!skipIfExist || !fs.existsSync(to)) try {
|
|
218
556
|
fs.cpSync(from, to, { recursive: true });
|
|
@@ -220,18 +558,34 @@ function copyAndSkipIfExist(from, to, skipIfExist) {
|
|
|
220
558
|
log.warn(`Copy failed: ${error}`, { timestamp: true });
|
|
221
559
|
}
|
|
222
560
|
}
|
|
223
|
-
|
|
224
561
|
//#endregion
|
|
225
|
-
//#region src/vite/build.ts
|
|
226
|
-
|
|
227
|
-
|
|
562
|
+
//#region src/vite/utils/build.ts
|
|
563
|
+
/**
|
|
564
|
+
* Build asar file and update package
|
|
565
|
+
* @param options - Asar build options
|
|
566
|
+
* @returns Buffer of the built asar file
|
|
567
|
+
*/
|
|
568
|
+
async function buildAsar(root, { version, asarOutputPath, electronDistPath, rendererDistPath, gzipPath, generateGzipFile }) {
|
|
569
|
+
electronDistPath = path.resolve(root, electronDistPath);
|
|
570
|
+
asarOutputPath = path.resolve(root, asarOutputPath);
|
|
571
|
+
rendererDistPath = path.resolve(root, rendererDistPath);
|
|
572
|
+
gzipPath = path.resolve(root, gzipPath);
|
|
573
|
+
const rPath = path.join(electronDistPath, "renderer");
|
|
574
|
+
await fs.promises.cp(rendererDistPath, rPath, { recursive: true });
|
|
228
575
|
fs.writeFileSync(path.join(electronDistPath, "version"), version);
|
|
576
|
+
await fs.promises.mkdir(path.dirname(asarOutputPath), { recursive: true });
|
|
229
577
|
await createPackage(electronDistPath, asarOutputPath);
|
|
230
578
|
const buf = await generateGzipFile(fs.readFileSync(asarOutputPath));
|
|
579
|
+
await fs.promises.mkdir(path.dirname(gzipPath), { recursive: true });
|
|
231
580
|
fs.writeFileSync(gzipPath, buf);
|
|
232
581
|
log.info(`Build update asar to '${gzipPath}' [${readableSize(buf.length)}]`, { timestamp: true });
|
|
233
582
|
return buf;
|
|
234
583
|
}
|
|
584
|
+
/**
|
|
585
|
+
* Build update.json file with signature and version information
|
|
586
|
+
* @param options - Version build options
|
|
587
|
+
* @param asarBuffer - Buffer of the asar file to sign
|
|
588
|
+
*/
|
|
235
589
|
async function buildUpdateJson({ versionPath, privateKey, cert, version, minimumVersion, generateSignature, generateUpdateJson }, asarBuffer) {
|
|
236
590
|
let _json = {
|
|
237
591
|
beta: {
|
|
@@ -251,388 +605,20 @@ async function buildUpdateJson({ versionPath, privateKey, cert, version, minimum
|
|
|
251
605
|
const sig = await generateSignature(asarBuffer, privateKey, cert, version);
|
|
252
606
|
_json = await generateUpdateJson(_json, sig, version, minimumVersion);
|
|
253
607
|
if (!isUpdateJSON(_json)) throw new Error("Invalid update json");
|
|
608
|
+
await fs.promises.mkdir(path.dirname(versionPath), { recursive: true });
|
|
254
609
|
fs.writeFileSync(versionPath, JSON.stringify(_json, null, 2));
|
|
255
610
|
log.info(`build update json to '${versionPath}'`, { timestamp: true });
|
|
256
611
|
}
|
|
257
|
-
|
|
258
|
-
//#endregion
|
|
259
|
-
//#region src/vite/bytecode/index.ts
|
|
260
|
-
function getBytecodeLoaderBlock(chunkFileName) {
|
|
261
|
-
return `require("${toRelativePath(bytecodeModuleLoader, normalizePath(chunkFileName))}");`;
|
|
262
|
-
}
|
|
263
|
-
/**
|
|
264
|
-
* Compile to v8 bytecode to protect source code.
|
|
265
|
-
*/
|
|
266
|
-
function bytecodePlugin(env, options) {
|
|
267
|
-
const { enable, preload = false, electronPath, beforeCompile } = options;
|
|
268
|
-
if (!enable) return null;
|
|
269
|
-
if (!preload && env === "preload") {
|
|
270
|
-
bytecodeLog.warn("`bytecodePlugin` is skiped in preload. To enable in preload, please manually set the \"enablePreload\" option to true and set `sandbox: false` when creating the window", { timestamp: true });
|
|
271
|
-
return null;
|
|
272
|
-
}
|
|
273
|
-
const filter = createFilter(/\.(m?[jt]s|[jt]sx)$/);
|
|
274
|
-
let config;
|
|
275
|
-
let bytecodeRequired = false;
|
|
276
|
-
let bytecodeFiles = [];
|
|
277
|
-
return {
|
|
278
|
-
name: `${bytecodeId}-${env}`,
|
|
279
|
-
apply: "build",
|
|
280
|
-
enforce: "post",
|
|
281
|
-
configResolved(resolvedConfig) {
|
|
282
|
-
config = resolvedConfig;
|
|
283
|
-
},
|
|
284
|
-
transform(code, id) {
|
|
285
|
-
if (!filter(id)) return convertLiteral(code, !!config.build.sourcemap);
|
|
286
|
-
},
|
|
287
|
-
generateBundle(options) {
|
|
288
|
-
if (options.format !== "es" && bytecodeRequired) this.emitFile({
|
|
289
|
-
type: "asset",
|
|
290
|
-
source: `${bytecodeModuleLoaderCode}\n`,
|
|
291
|
-
name: "Bytecode Loader File",
|
|
292
|
-
fileName: bytecodeModuleLoader
|
|
293
|
-
});
|
|
294
|
-
},
|
|
295
|
-
renderChunk(code, chunk, options) {
|
|
296
|
-
if (options.format === "es") {
|
|
297
|
-
bytecodeLog.warn("`bytecodePlugin` does not support ES module, please set \"build.rollupOptions.output.format\" option to \"cjs\"", { timestamp: true });
|
|
298
|
-
return null;
|
|
299
|
-
}
|
|
300
|
-
if (chunk.type === "chunk") {
|
|
301
|
-
bytecodeRequired = true;
|
|
302
|
-
return convertArrowFunctionAndTemplate(code);
|
|
303
|
-
}
|
|
304
|
-
return null;
|
|
305
|
-
},
|
|
306
|
-
async writeBundle(options, output) {
|
|
307
|
-
if (options.format === "es" || !bytecodeRequired) return;
|
|
308
|
-
const outDir = options.dir;
|
|
309
|
-
bytecodeFiles = [];
|
|
310
|
-
const bundles = Object.keys(output);
|
|
311
|
-
const chunks = Object.values(output).filter((chunk) => chunk.type === "chunk" && chunk.fileName !== bytecodeModuleLoader);
|
|
312
|
-
const bytecodeChunks = new Set(chunks.map((chunk) => chunk.fileName));
|
|
313
|
-
const pattern = chunks.filter((chunk) => !chunk.isEntry).map((chunk) => path.basename(chunk.fileName)).map((chunk) => `(${chunk})`).join("|");
|
|
314
|
-
const bytecodeRE = pattern ? new RegExp(`require\\(\\S*(?=(${pattern})\\S*\\))`, "g") : null;
|
|
315
|
-
await Promise.all(bundles.map(async (name) => {
|
|
316
|
-
const chunk = output[name];
|
|
317
|
-
if (chunk.type === "chunk") {
|
|
318
|
-
let _code = chunk.code;
|
|
319
|
-
const chunkFilePath = path.resolve(outDir, name);
|
|
320
|
-
if (beforeCompile) {
|
|
321
|
-
const cbResult = await beforeCompile(_code, chunkFilePath);
|
|
322
|
-
if (cbResult) _code = cbResult;
|
|
323
|
-
}
|
|
324
|
-
if (bytecodeRE && _code.match(bytecodeRE)) {
|
|
325
|
-
let match;
|
|
326
|
-
const s = new MagicString(_code);
|
|
327
|
-
while (match = bytecodeRE.exec(_code)) {
|
|
328
|
-
const [prefix, chunkName] = match;
|
|
329
|
-
const len = prefix.length + chunkName.length;
|
|
330
|
-
s.overwrite(match.index, match.index + len, `${prefix + chunkName}c`, { contentOnly: true });
|
|
331
|
-
}
|
|
332
|
-
_code = s.toString();
|
|
333
|
-
}
|
|
334
|
-
if (bytecodeChunks.has(name)) {
|
|
335
|
-
const bytecodeBuffer = await compileToBytecode(_code, electronPath);
|
|
336
|
-
fs.writeFileSync(`${chunkFilePath}c`, bytecodeBuffer);
|
|
337
|
-
if (chunk.isEntry) {
|
|
338
|
-
const code = `${useStrict}\n${getBytecodeLoaderBlock(chunk.fileName)}\nmodule.exports=${`require("./${`${path.basename(name)}c`}");`}\n`;
|
|
339
|
-
fs.writeFileSync(chunkFilePath, code);
|
|
340
|
-
} else fs.unlinkSync(chunkFilePath);
|
|
341
|
-
bytecodeFiles.push({
|
|
342
|
-
name: `${name}c`,
|
|
343
|
-
size: bytecodeBuffer.length
|
|
344
|
-
});
|
|
345
|
-
} else {
|
|
346
|
-
if (chunk.isEntry) {
|
|
347
|
-
let hasBytecodeMoudle = false;
|
|
348
|
-
const idsToHandle = new Set([...chunk.imports, ...chunk.dynamicImports]);
|
|
349
|
-
for (const moduleId of idsToHandle) {
|
|
350
|
-
if (bytecodeChunks.has(moduleId)) {
|
|
351
|
-
hasBytecodeMoudle = true;
|
|
352
|
-
break;
|
|
353
|
-
}
|
|
354
|
-
const moduleInfo = this.getModuleInfo(moduleId);
|
|
355
|
-
if (moduleInfo) {
|
|
356
|
-
const { importers, dynamicImporters } = moduleInfo;
|
|
357
|
-
for (const importerId of importers) idsToHandle.add(importerId);
|
|
358
|
-
for (const importerId of dynamicImporters) idsToHandle.add(importerId);
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
const bytecodeLoaderBlock = getBytecodeLoaderBlock(chunk.fileName);
|
|
362
|
-
_code = hasBytecodeMoudle ? _code.replace(new RegExp(`(${useStrict})|("use strict";)`), `${useStrict}\n${bytecodeLoaderBlock}`) : _code;
|
|
363
|
-
}
|
|
364
|
-
fs.writeFileSync(chunkFilePath, _code);
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
}));
|
|
368
|
-
},
|
|
369
|
-
closeBundle() {
|
|
370
|
-
const outDir = `${normalizePath(path.relative(config.root, path.resolve(config.root, config.build.outDir)))}/`;
|
|
371
|
-
bytecodeFiles.forEach((file) => {
|
|
372
|
-
bytecodeLog.info(`${outDir}${file.name} [${readableSize(file.size)}]`, { timestamp: true });
|
|
373
|
-
});
|
|
374
|
-
bytecodeLog.info(`${bytecodeFiles.length} bundles compiled into bytecode.`, { timestamp: true });
|
|
375
|
-
bytecodeFiles = [];
|
|
376
|
-
}
|
|
377
|
-
};
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
//#endregion
|
|
381
|
-
//#region src/vite/electron/utils.ts
|
|
382
|
-
/** Resolve the default Vite's `InlineConfig` for build Electron-Main */
|
|
383
|
-
function resolveViteConfig(isESM, options) {
|
|
384
|
-
return mergeConfig({
|
|
385
|
-
configFile: false,
|
|
386
|
-
publicDir: false,
|
|
387
|
-
build: {
|
|
388
|
-
lib: options.entry && {
|
|
389
|
-
entry: options.entry,
|
|
390
|
-
formats: isESM ? ["es"] : ["cjs"],
|
|
391
|
-
fileName: () => "[name].js"
|
|
392
|
-
},
|
|
393
|
-
outDir: "dist-electron",
|
|
394
|
-
emptyOutDir: false
|
|
395
|
-
},
|
|
396
|
-
resolve: {
|
|
397
|
-
conditions: ["node"],
|
|
398
|
-
mainFields: [
|
|
399
|
-
"module",
|
|
400
|
-
"jsnext:main",
|
|
401
|
-
"jsnext"
|
|
402
|
-
]
|
|
403
|
-
},
|
|
404
|
-
define: { "process.env": "process.env" }
|
|
405
|
-
}, options?.vite || {});
|
|
406
|
-
}
|
|
407
|
-
/**
|
|
408
|
-
* Inspired `tree-kill`, implemented based on sync-api. #168
|
|
409
|
-
* @see https://github.com/pkrumins/node-tree-kill/blob/v1.2.2/index.js
|
|
410
|
-
*/
|
|
411
|
-
function treeKillSync(pid) {
|
|
412
|
-
if (process.platform === "win32") cp.execSync(`taskkill /pid ${pid} /T /F`);
|
|
413
|
-
else killTree(pidTree({
|
|
414
|
-
pid,
|
|
415
|
-
ppid: process.pid
|
|
416
|
-
}));
|
|
417
|
-
}
|
|
418
|
-
function pidTree(tree) {
|
|
419
|
-
const command = process.platform === "darwin" ? `pgrep -P ${tree.pid}` : `ps -o pid --no-headers --ppid ${tree.ppid}`;
|
|
420
|
-
try {
|
|
421
|
-
const childs = cp.execSync(command, { encoding: "utf8" }).match(/\d+/g)?.map((id) => +id);
|
|
422
|
-
if (childs) tree.children = childs.map((cid) => pidTree({
|
|
423
|
-
pid: cid,
|
|
424
|
-
ppid: tree.pid
|
|
425
|
-
}));
|
|
426
|
-
} catch {}
|
|
427
|
-
return tree;
|
|
428
|
-
}
|
|
429
|
-
function killTree(tree) {
|
|
430
|
-
if (tree.children) for (const child of tree.children) killTree(child);
|
|
431
|
-
try {
|
|
432
|
-
process.kill(tree.pid);
|
|
433
|
-
} catch {}
|
|
434
|
-
}
|
|
435
|
-
|
|
436
612
|
//#endregion
|
|
437
|
-
//#region src/vite/
|
|
438
|
-
function build$1(isESM, options) {
|
|
439
|
-
return build(resolveViteConfig(isESM, options));
|
|
440
|
-
}
|
|
441
|
-
function electron(isESM, root, options) {
|
|
442
|
-
const optionsArray = Array.isArray(options) ? options : [options];
|
|
443
|
-
let userConfig;
|
|
444
|
-
let configEnv;
|
|
445
|
-
if (!version.startsWith("8.")) throw new Error(`[vite-plugin-electron] Vite v${version} does not support \`rolldownOptions\`, please install \`vite@>=8\` or use an earlier version of \`vite-plugin-electron\`.`);
|
|
446
|
-
async function parallelBuild(options) {
|
|
447
|
-
await Promise.all(options.map(build$1.bind(build$1, isESM)));
|
|
448
|
-
}
|
|
449
|
-
return [{
|
|
450
|
-
name: "vite-plugin-electron:dev",
|
|
451
|
-
apply: "serve",
|
|
452
|
-
configResolved(config) {
|
|
453
|
-
if (config.root !== root) throw new Error(`Renderer's root (${config.root}) is not same as electron's root (${root}). Please setup \`root\` in electron plugin`);
|
|
454
|
-
},
|
|
455
|
-
configureServer(server) {
|
|
456
|
-
server.httpServer?.once("listening", () => {
|
|
457
|
-
Object.assign(process.env, { VITE_DEV_SERVER_URL: server.resolvedUrls?.local[0] });
|
|
458
|
-
const entryCount = optionsArray.length;
|
|
459
|
-
let closeBundleCount = 0;
|
|
460
|
-
parallelBuild(optionsArray.map((options) => {
|
|
461
|
-
options.vite ??= {};
|
|
462
|
-
options.vite.mode ??= server.config.mode;
|
|
463
|
-
options.vite.root ??= server.config.root;
|
|
464
|
-
options.vite.envDir ??= server.config.envDir;
|
|
465
|
-
options.vite.envPrefix ??= server.config.envPrefix;
|
|
466
|
-
const defaultArgs = [options.vite.root || ".", "--no-sandbox"];
|
|
467
|
-
options.vite.build ??= {};
|
|
468
|
-
if (!Object.keys(options.vite.build).includes("watch")) options.vite.build.watch = {};
|
|
469
|
-
options.vite.build.minify ??= false;
|
|
470
|
-
options.vite.plugins ??= [];
|
|
471
|
-
options.vite.plugins.push({
|
|
472
|
-
name: ":startup",
|
|
473
|
-
closeBundle() {
|
|
474
|
-
if (++closeBundleCount < entryCount) return;
|
|
475
|
-
if (options.onstart) options.onstart.call(this, {
|
|
476
|
-
async startup(args = defaultArgs, ...opt) {
|
|
477
|
-
await startup(args, ...opt);
|
|
478
|
-
},
|
|
479
|
-
reload() {
|
|
480
|
-
if (process.electronApp) {
|
|
481
|
-
(server.hot || server.ws).send({ type: "full-reload" });
|
|
482
|
-
startup.send("electron-vite&type=hot-reload");
|
|
483
|
-
} else startup(defaultArgs);
|
|
484
|
-
}
|
|
485
|
-
});
|
|
486
|
-
else startup(defaultArgs);
|
|
487
|
-
}
|
|
488
|
-
});
|
|
489
|
-
return options;
|
|
490
|
-
}));
|
|
491
|
-
});
|
|
492
|
-
}
|
|
493
|
-
}, {
|
|
494
|
-
name: "vite-plugin-electron:prod",
|
|
495
|
-
apply: "build",
|
|
496
|
-
config(config, env) {
|
|
497
|
-
userConfig = config;
|
|
498
|
-
configEnv = env;
|
|
499
|
-
config.base ??= "./";
|
|
500
|
-
},
|
|
501
|
-
async closeBundle() {
|
|
502
|
-
await parallelBuild(optionsArray.map((options) => {
|
|
503
|
-
options.vite ??= {};
|
|
504
|
-
options.vite.mode ??= configEnv.mode;
|
|
505
|
-
options.vite.root ??= userConfig.root;
|
|
506
|
-
options.vite.envDir ??= userConfig.envDir;
|
|
507
|
-
options.vite.envPrefix ??= userConfig.envPrefix;
|
|
508
|
-
return options;
|
|
509
|
-
}));
|
|
510
|
-
}
|
|
511
|
-
}];
|
|
512
|
-
}
|
|
513
|
-
/**
|
|
514
|
-
* Electron App startup function.
|
|
515
|
-
* It will mount the Electron App child-process to `process.electronApp`.
|
|
516
|
-
* @param argv default value `['.', '--no-sandbox']`
|
|
517
|
-
* @param options options for `child_process.spawn`
|
|
518
|
-
* @param customElectronPkg custom electron package name (default: 'electron')
|
|
519
|
-
*/
|
|
520
|
-
const startup = async (argv = [".", "--no-sandbox"], options, customElectronPkg) => {
|
|
521
|
-
const { spawn } = await import("node:child_process");
|
|
522
|
-
const electron = await import(customElectronPkg ?? "electron");
|
|
523
|
-
const electronPath = electron.default ?? electron;
|
|
524
|
-
await startup.exit();
|
|
525
|
-
const stdio = process.platform === "linux" ? [
|
|
526
|
-
"inherit",
|
|
527
|
-
"inherit",
|
|
528
|
-
"inherit",
|
|
529
|
-
"ignore",
|
|
530
|
-
"ipc"
|
|
531
|
-
] : [
|
|
532
|
-
"inherit",
|
|
533
|
-
"inherit",
|
|
534
|
-
"inherit",
|
|
535
|
-
"ipc"
|
|
536
|
-
];
|
|
537
|
-
process.electronApp = spawn(electronPath, argv, {
|
|
538
|
-
stdio,
|
|
539
|
-
...options
|
|
540
|
-
});
|
|
541
|
-
process.electronApp.once("exit", process.exit);
|
|
542
|
-
if (!startup.hookedProcessExit) {
|
|
543
|
-
startup.hookedProcessExit = true;
|
|
544
|
-
process.once("exit", startup.exit);
|
|
545
|
-
}
|
|
546
|
-
};
|
|
547
|
-
startup.send = (message) => {
|
|
548
|
-
if (process.electronApp) process.electronApp.send?.(message);
|
|
549
|
-
};
|
|
550
|
-
startup.hookedProcessExit = false;
|
|
551
|
-
startup.exit = async () => {
|
|
552
|
-
if (process.electronApp) await new Promise((resolve) => {
|
|
553
|
-
process.electronApp.removeAllListeners();
|
|
554
|
-
process.electronApp.once("exit", resolve);
|
|
555
|
-
treeKillSync(process.electronApp.pid);
|
|
556
|
-
});
|
|
557
|
-
};
|
|
558
|
-
|
|
559
|
-
//#endregion
|
|
560
|
-
//#region src/vite/electron/plugin.ts
|
|
561
|
-
/**
|
|
562
|
-
* @see https://github.com/vitejs/vite/blob/v4.4.7/packages/vite/src/node/utils.ts#L140
|
|
563
|
-
*/
|
|
564
|
-
const bareImportRE = /^(?![a-zA-Z]:)[\w@](?!.*:\/\/)/;
|
|
565
|
-
const nodeModulesRE = /\/node_modules\//;
|
|
566
|
-
/**
|
|
567
|
-
* During dev, we exclude the `cjs` npm-pkg from bundle, mush like Vite :)
|
|
568
|
-
*/
|
|
569
|
-
function notBundle(options = {}) {
|
|
570
|
-
const externalIds = /* @__PURE__ */ new Set();
|
|
571
|
-
return {
|
|
572
|
-
name: "vite-plugin-electron:not-bundle",
|
|
573
|
-
enforce: "pre",
|
|
574
|
-
apply: "serve",
|
|
575
|
-
resolveId: {
|
|
576
|
-
filter: { id: bareImportRE },
|
|
577
|
-
async handler(source, importer) {
|
|
578
|
-
if (!importer || importer.includes("node_modules/")) return;
|
|
579
|
-
if (externalIds.has(source)) return {
|
|
580
|
-
id: source,
|
|
581
|
-
external: true
|
|
582
|
-
};
|
|
583
|
-
const id = (await this.resolve(source, importer, { skipSelf: true }))?.id;
|
|
584
|
-
if (!id || !nodeModulesRE.test(id) || options.filter?.(id) === false) return;
|
|
585
|
-
try {
|
|
586
|
-
createRequire(importer).resolve(source);
|
|
587
|
-
} catch {
|
|
588
|
-
return;
|
|
589
|
-
}
|
|
590
|
-
externalIds.add(source);
|
|
591
|
-
return {
|
|
592
|
-
id: source,
|
|
593
|
-
external: true,
|
|
594
|
-
moduleSideEffects: false
|
|
595
|
-
};
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
};
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
//#endregion
|
|
602
|
-
//#region src/utils/crypto.ts
|
|
603
|
-
function hashBuffer(data, length) {
|
|
604
|
-
const hash = crypto.createHash("SHA256").update(data).digest("binary");
|
|
605
|
-
return Buffer.from(hash).subarray(0, length);
|
|
606
|
-
}
|
|
607
|
-
function aesEncrypt(plainText, key, iv) {
|
|
608
|
-
const cipher = crypto.createCipheriv("aes-256-cbc", key, iv);
|
|
609
|
-
return cipher.update(plainText, "utf8", "base64url") + cipher.final("base64url");
|
|
610
|
-
}
|
|
611
|
-
/**
|
|
612
|
-
* Default function to generate asar signature, returns generated signature
|
|
613
|
-
* @param buffer file buffer
|
|
614
|
-
* @param privateKey primary key
|
|
615
|
-
* @param cert certificate
|
|
616
|
-
* @param version target version
|
|
617
|
-
*/
|
|
618
|
-
function defaultSignature(buffer, privateKey, cert, version) {
|
|
619
|
-
return aesEncrypt(`${crypto.createSign("RSA-SHA256").update(buffer).sign(crypto.createPrivateKey(privateKey), "base64")}%${version}`, hashBuffer(cert, 32), hashBuffer(buffer, 16));
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
//#endregion
|
|
623
|
-
//#region src/utils/zip.ts
|
|
613
|
+
//#region src/vite/utils/key.ts
|
|
624
614
|
/**
|
|
625
|
-
*
|
|
626
|
-
* @param
|
|
615
|
+
* Generate a new key pair for signing
|
|
616
|
+
* @param keyLength - Key length in bits
|
|
617
|
+
* @param subject - Certificate subject
|
|
618
|
+
* @param days - Validity period in days
|
|
619
|
+
* @param privateKeyPath - Output path for private key
|
|
620
|
+
* @param certPath - Output path for certificate
|
|
627
621
|
*/
|
|
628
|
-
async function defaultZipFile(buffer) {
|
|
629
|
-
return new Promise((resolve, reject) => {
|
|
630
|
-
zlib.brotliCompress(buffer, (err, buffer) => err ? reject(err) : resolve(buffer));
|
|
631
|
-
});
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
//#endregion
|
|
635
|
-
//#region src/vite/key.ts
|
|
636
622
|
async function generateKeyPair(keyLength, subject, days, privateKeyPath, certPath) {
|
|
637
623
|
const privateKeyDir = path.dirname(privateKeyPath);
|
|
638
624
|
if (!fs.existsSync(privateKeyDir)) fs.mkdirSync(privateKeyDir, { recursive: true });
|
|
@@ -650,6 +636,11 @@ async function generateKeyPair(keyLength, subject, days, privateKeyPath, certPat
|
|
|
650
636
|
fs.writeFileSync(privateKeyPath, privateKey.replace(/\r\n?/g, "\n"));
|
|
651
637
|
fs.writeFileSync(certPath, cert.replace(/\r\n?/g, "\n"));
|
|
652
638
|
}
|
|
639
|
+
/**
|
|
640
|
+
* Parse and load keys, generating new ones if they don't exist
|
|
641
|
+
* @param options - Key parsing options
|
|
642
|
+
* @returns Object containing private key and certificate strings
|
|
643
|
+
*/
|
|
653
644
|
async function parseKeys({ keyLength, privateKeyPath, certPath, subject, days }) {
|
|
654
645
|
const keysDir = path.dirname(privateKeyPath);
|
|
655
646
|
let privateKey = process.env.UPDATER_PK;
|
|
@@ -673,37 +664,43 @@ async function parseKeys({ keyLength, privateKeyPath, certPath, subject, days })
|
|
|
673
664
|
cert
|
|
674
665
|
};
|
|
675
666
|
}
|
|
667
|
+
/**
|
|
668
|
+
* Convert DistinguishedName object to CertSubject array
|
|
669
|
+
* @param subject - Distinguished name object
|
|
670
|
+
* @returns Certificate subject array
|
|
671
|
+
*/
|
|
676
672
|
function parseSubjects(subject) {
|
|
677
673
|
return Object.entries(subject).filter(([_, value]) => !!value).map(([name, value]) => ({
|
|
678
674
|
name,
|
|
679
675
|
value
|
|
680
676
|
}));
|
|
681
677
|
}
|
|
682
|
-
|
|
683
678
|
//#endregion
|
|
684
|
-
//#region src/vite/
|
|
685
|
-
async function
|
|
686
|
-
const { minimumVersion = "0.0.0", paths: { asarOutputPath = `release/${pkg.name}.asar`, gzipPath = `release/${pkg.name}-${pkg.version}.asar.gz`, entryOutDir = "dist-entry", electronDistPath = "dist-electron", rendererDistPath = "dist", versionPath = "version.json" } = {}, keys: { privateKeyPath = "keys/private.pem", certPath = "keys/cert.pem", keyLength = 2048, certInfo: { subject = {
|
|
679
|
+
//#region src/vite/core.ts
|
|
680
|
+
async function resolveUpdaterOption(root, pkg, options = {}, resolveSignatureKeys = true) {
|
|
681
|
+
const { minimumVersion = "0.0.0", paths: { asarOutputPath = `release/${pkg.name}.asar`, gzipPath = `release/${pkg.name}-${pkg.version}.asar.gz`, entryOutDir = "dist-entry", electronDistPath = "dist-electron", rendererDistPath = "dist", versionPath = "release/version.json" } = {}, keys: { privateKeyPath = "keys/private.pem", certPath = "keys/cert.pem", keyLength = 2048, certInfo: { subject = {
|
|
687
682
|
commonName: pkg.name,
|
|
688
683
|
organizationName: `org.${pkg.name}`
|
|
689
684
|
}, days = 3650 } = {} } = {}, overrideGenerator: { generateGzipFile = defaultZipFile, generateSignature = defaultSignature, generateUpdateJson = defaultVersionJsonGenerator } = {} } = options;
|
|
690
|
-
const
|
|
691
|
-
version: pkg.version,
|
|
692
|
-
asarOutputPath,
|
|
693
|
-
gzipPath,
|
|
694
|
-
electronDistPath,
|
|
695
|
-
rendererDistPath,
|
|
696
|
-
generateGzipFile
|
|
697
|
-
};
|
|
698
|
-
const { privateKey, cert } = await parseKeys({
|
|
685
|
+
const { privateKey, cert } = resolveSignatureKeys ? await parseKeys({
|
|
699
686
|
keyLength,
|
|
700
|
-
privateKeyPath,
|
|
701
|
-
certPath,
|
|
687
|
+
privateKeyPath: path.resolve(root, privateKeyPath),
|
|
688
|
+
certPath: path.resolve(root, certPath),
|
|
702
689
|
subject,
|
|
703
690
|
days
|
|
704
|
-
})
|
|
691
|
+
}) : {
|
|
692
|
+
privateKey: "",
|
|
693
|
+
cert: ""
|
|
694
|
+
};
|
|
705
695
|
return {
|
|
706
|
-
buildAsarOption
|
|
696
|
+
buildAsarOption: {
|
|
697
|
+
version: pkg.version,
|
|
698
|
+
asarOutputPath,
|
|
699
|
+
gzipPath,
|
|
700
|
+
electronDistPath,
|
|
701
|
+
rendererDistPath,
|
|
702
|
+
generateGzipFile
|
|
703
|
+
},
|
|
707
704
|
buildVersionOption: {
|
|
708
705
|
version: pkg.version,
|
|
709
706
|
minimumVersion,
|
|
@@ -713,289 +710,230 @@ async function parseUpdaterOption(pkg, options = {}) {
|
|
|
713
710
|
generateSignature,
|
|
714
711
|
generateUpdateJson
|
|
715
712
|
},
|
|
716
|
-
cert,
|
|
717
713
|
entryOutDir
|
|
718
714
|
};
|
|
719
715
|
}
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
* @example
|
|
727
|
-
* import { debugStartup, buildElectronPluginOptions } from 'electron-incremental-update/vite'
|
|
728
|
-
* const options = buildElectronPluginOptions({
|
|
729
|
-
* // ...
|
|
730
|
-
* main: {
|
|
731
|
-
* // ...
|
|
732
|
-
* startup: debugStartup
|
|
733
|
-
* },
|
|
734
|
-
* })
|
|
735
|
-
*/
|
|
736
|
-
const debugStartup = async (args) => {
|
|
737
|
-
if (process.env.VSCODE_DEBUG) console.log("[startup] Electron App");
|
|
738
|
-
else await args.startup();
|
|
739
|
-
};
|
|
740
|
-
/**
|
|
741
|
-
* Startup function to filter unwanted error message
|
|
742
|
-
* @see {@link https://github.com/electron/electron/issues/46903#issuecomment-2848483520 reference}
|
|
743
|
-
* @example
|
|
744
|
-
* import { filterErrorMessageStartup, buildElectronPluginOptions } from 'electron-incremental-update/vite'
|
|
745
|
-
* const options = buildElectronPluginOptions({
|
|
746
|
-
* // ...
|
|
747
|
-
* main: {
|
|
748
|
-
* // ...
|
|
749
|
-
* startup: args => filterErrorMessageStartup(
|
|
750
|
-
* args,
|
|
751
|
-
* // ignore error message when function returns false
|
|
752
|
-
* msg => !/"code":-32601/.test(msg)
|
|
753
|
-
* )
|
|
754
|
-
* },
|
|
755
|
-
* })
|
|
756
|
-
*/
|
|
757
|
-
async function filterErrorMessageStartup(args, filter) {
|
|
758
|
-
const stdio = process.platform === "linux" ? [
|
|
759
|
-
"inherit",
|
|
760
|
-
"pipe",
|
|
761
|
-
"pipe",
|
|
762
|
-
"ignore",
|
|
763
|
-
"ipc"
|
|
764
|
-
] : [
|
|
765
|
-
"inherit",
|
|
766
|
-
"pipe",
|
|
767
|
-
"pipe",
|
|
768
|
-
"ipc"
|
|
769
|
-
];
|
|
770
|
-
await args.startup(void 0, { stdio });
|
|
771
|
-
const elec = process.electronApp;
|
|
772
|
-
elec.stdout.addListener("data", (data) => {
|
|
773
|
-
console.log(data.toString().trimEnd());
|
|
774
|
-
});
|
|
775
|
-
elec.stderr.addListener("data", (data) => {
|
|
776
|
-
const message = data.toString();
|
|
777
|
-
if (filter(message)) console.error(message);
|
|
778
|
-
});
|
|
779
|
-
}
|
|
780
|
-
/**
|
|
781
|
-
* Startup function util to fix Windows terminal charset
|
|
782
|
-
* @example
|
|
783
|
-
* import { debugStartup, fixWinCharEncoding, buildElectronPluginOptions } from 'electron-incremental-update/vite'
|
|
784
|
-
* const options = buildElectronPluginOptions({
|
|
785
|
-
* // ...
|
|
786
|
-
* main: {
|
|
787
|
-
* // ...
|
|
788
|
-
* startup: fixWinCharEncoding(debugStartup)
|
|
789
|
-
* },
|
|
790
|
-
* })
|
|
791
|
-
*/
|
|
792
|
-
function fixWinCharEncoding(fn) {
|
|
793
|
-
return (async (...args) => {
|
|
794
|
-
if (process.platform === "win32") (await import("node:child_process")).spawnSync("chcp", ["65001"]);
|
|
795
|
-
await fn(...args);
|
|
796
|
-
});
|
|
797
|
-
}
|
|
798
|
-
function getMainFileBaseName(options) {
|
|
799
|
-
let mainFilePath;
|
|
800
|
-
if (typeof options === "string") mainFilePath = path.basename(options);
|
|
801
|
-
else if (Array.isArray(options)) mainFilePath = path.basename(options[0]);
|
|
802
|
-
else {
|
|
803
|
-
if (!(options?.index ?? options?.main)) throw new Error(`\`options.main.files\` (${options}) must have "index" or "main" key, like \`{ index: "./electron/main/index.ts" }\``);
|
|
804
|
-
mainFilePath = options?.index ? "index.js" : "main.js";
|
|
716
|
+
function resolveEntryName(files) {
|
|
717
|
+
if (typeof files === "string") return path.parse(files).name;
|
|
718
|
+
if (Array.isArray(files)) {
|
|
719
|
+
const [firstInput] = files;
|
|
720
|
+
if (!firstInput) throw new Error("`options.main.files` must contain at least one main entry");
|
|
721
|
+
return path.parse(firstInput).name;
|
|
805
722
|
}
|
|
806
|
-
|
|
807
|
-
|
|
723
|
+
const firstEntry = Object.entries(files)[0];
|
|
724
|
+
if (!firstEntry) throw new Error("`options.main.files` must contain at least one main entry");
|
|
725
|
+
return firstEntry[0];
|
|
808
726
|
}
|
|
809
|
-
function
|
|
727
|
+
function normalizeVersionPath(versionPath) {
|
|
810
728
|
versionPath = normalizePath(versionPath);
|
|
811
729
|
if (!versionPath.startsWith("./")) versionPath = `./${versionPath}`;
|
|
812
730
|
return new URL(versionPath, "file://").pathname.slice(1);
|
|
813
731
|
}
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
/^node:/,
|
|
818
|
-
/.*\.(node|dll|dylib|so)$/,
|
|
819
|
-
"original-fs"
|
|
820
|
-
];
|
|
821
|
-
/**
|
|
822
|
-
* Base on `./electron/simple`
|
|
823
|
-
* - integrate with updater
|
|
824
|
-
* - no `renderer` config
|
|
825
|
-
* - remove old output file
|
|
826
|
-
* - externalize dependencies
|
|
827
|
-
* - auto restart when entry file changes
|
|
828
|
-
* - other configs in {@link https://github.com/electron-vite/electron-vite-vue/blob/main/vite.config.ts electron-vite-vue template}
|
|
829
|
-
*
|
|
830
|
-
* You can override all the vite configs, except output directories (use `options.updater.paths.electronDistPath` instead)
|
|
831
|
-
*
|
|
832
|
-
* @example
|
|
833
|
-
* ```ts
|
|
834
|
-
* import { defineConfig } from 'vite'
|
|
835
|
-
* import { debugStartup, electronWithUpdater } from 'electron-incremental-update/vite'
|
|
836
|
-
*
|
|
837
|
-
* export default defineConfig(async ({ command }) => {
|
|
838
|
-
* const isBuild = command === 'build'
|
|
839
|
-
* return {
|
|
840
|
-
* plugins: [
|
|
841
|
-
* electronWithUpdater({
|
|
842
|
-
* isBuild,
|
|
843
|
-
* main: {
|
|
844
|
-
* files: ['./electron/main/index.ts', './electron/main/worker.ts'],
|
|
845
|
-
* // see https://github.com/electron-vite/electron-vite-vue/blob/85ed267c4851bf59f32888d766c0071661d4b94c/vite.config.ts#L22-L28
|
|
846
|
-
* onstart: debugStartup,
|
|
847
|
-
* },
|
|
848
|
-
* preload: {
|
|
849
|
-
* files: './electron/preload/index.ts',
|
|
850
|
-
* },
|
|
851
|
-
* updater: {
|
|
852
|
-
* // options
|
|
853
|
-
* }
|
|
854
|
-
* }),
|
|
855
|
-
* ],
|
|
856
|
-
* server: process.env.VSCODE_DEBUG && (() => {
|
|
857
|
-
* const url = new URL(pkg.debug.env.VITE_DEV_SERVER_URL)
|
|
858
|
-
* return {
|
|
859
|
-
* host: url.hostname,
|
|
860
|
-
* port: +url.port,
|
|
861
|
-
* }
|
|
862
|
-
* })(),
|
|
863
|
-
* }
|
|
864
|
-
* })
|
|
865
|
-
* ```
|
|
866
|
-
*/
|
|
867
|
-
async function electronWithUpdater(options) {
|
|
868
|
-
let { isBuild, root = process.cwd(), external, entry: _entry, main: _main, preload: _preload, sourcemap = !isBuild || !!process.env.VSCODE_DEBUG, minify = isBuild, buildVersionJson, updater, bytecode, useNotBundle = true } = options;
|
|
869
|
-
const pkg = await loadPackageJSON(root);
|
|
732
|
+
async function createElectronOptions(options, context) {
|
|
733
|
+
const { entry, main, preload, sourcemap = context.isDev || !!process.env.VSCODE_DEBUG, minify = !context.isDev, buildVersionJson, notBundle = true, external, updater, bytecode, localDevUpdate } = options;
|
|
734
|
+
const pkg = context.packageJson;
|
|
870
735
|
if (!pkg || !pkg.version || !pkg.name || !pkg.main) throw new Error("package.json not found or invalid, must contains version, name and main field");
|
|
871
736
|
const isESM = pkg.type === "module";
|
|
872
|
-
const finalExternal = [...defaultExternal
|
|
873
|
-
|
|
737
|
+
const finalExternal = [...defaultExternal];
|
|
738
|
+
if (external === true) finalExternal.push(...Object.keys(pkg.dependencies || {}));
|
|
739
|
+
else if (Array.isArray(external)) finalExternal.push(...external);
|
|
740
|
+
const bytecodeOptions = typeof bytecode === "object" ? {
|
|
741
|
+
...bytecode,
|
|
742
|
+
enable: bytecode.enable ?? true
|
|
743
|
+
} : bytecode === true ? { enable: true } : void 0;
|
|
874
744
|
if (isESM && bytecodeOptions?.enable) throw new Error("`bytecodePlugin` does not support ES module, please remove \"type\": \"module\" in package.json");
|
|
875
|
-
const
|
|
745
|
+
const resolvedLocalDevUpdate = context.isDev ? resolveLocalDevUpdateOptions(context.root, localDevUpdate) : void 0;
|
|
746
|
+
const updatePkg = await resolveLocalDevUpdatePackage(pkg, resolvedLocalDevUpdate);
|
|
747
|
+
const { buildAsarOption, buildVersionOption, entryOutDir } = await resolveUpdaterOption(context.root, updatePkg, updater, !resolvedLocalDevUpdate);
|
|
748
|
+
const mainFileName = `${resolveEntryName(main.files)}.${isESM ? "mjs" : "js"}`;
|
|
749
|
+
log.info(`Using "${mainFileName}" as main file`, { timestamp: true });
|
|
876
750
|
log.info(`Clear cache files`, { timestamp: true });
|
|
877
|
-
await Promise.all([
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
751
|
+
await Promise.all([
|
|
752
|
+
buildAsarOption.rendererDistPath,
|
|
753
|
+
buildAsarOption.electronDistPath,
|
|
754
|
+
entryOutDir
|
|
755
|
+
].map((p) => fs.promises.rm(path.resolve(context.root, p), {
|
|
881
756
|
recursive: true,
|
|
882
757
|
force: true
|
|
883
|
-
})
|
|
758
|
+
}))).catch(() => {});
|
|
759
|
+
const outputNames = {
|
|
760
|
+
entryFileNames: `[name].${isESM ? "mjs" : "js"}`,
|
|
761
|
+
chunkFileNames: `[name].${isESM ? "mjs" : "js"}`,
|
|
762
|
+
assetFileNames: "[name].[ext]"
|
|
763
|
+
};
|
|
764
|
+
const versionPath = normalizeVersionPath(normalizePath(buildVersionOption.versionPath));
|
|
765
|
+
const mainOnstart = resolvedLocalDevUpdate ? createLocalDevUpdateOnstart({
|
|
766
|
+
root: context.root,
|
|
767
|
+
pkg: updatePkg,
|
|
768
|
+
buildAsarOption,
|
|
769
|
+
versionPath,
|
|
770
|
+
minimumVersion: buildVersionOption.minimumVersion,
|
|
771
|
+
localDevUpdate: resolvedLocalDevUpdate,
|
|
772
|
+
userOnstart: main.onstart
|
|
773
|
+
}) : main.onstart;
|
|
884
774
|
const define = {
|
|
885
775
|
__EIU_ASAR_BASE_NAME__: JSON.stringify(path.basename(buildAsarOption.asarOutputPath)),
|
|
886
776
|
__EIU_ELECTRON_DIST_PATH__: JSON.stringify(normalizePath(buildAsarOption.electronDistPath)),
|
|
887
777
|
__EIU_ENTRY_DIST_PATH__: JSON.stringify(normalizePath(entryOutDir)),
|
|
888
|
-
__EIU_IS_DEV__: JSON.stringify(
|
|
778
|
+
__EIU_IS_DEV__: JSON.stringify(context.isDev),
|
|
889
779
|
__EIU_IS_ESM__: JSON.stringify(isESM),
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
780
|
+
__EIU_LOCAL_DEV_UPDATE__: JSON.stringify(!!resolvedLocalDevUpdate),
|
|
781
|
+
__EIU_LOCAL_DEV_UPDATE_ASAR_PATH__: JSON.stringify(resolvedLocalDevUpdate?.installedAsarPath ?? ""),
|
|
782
|
+
__EIU_LOCAL_DEV_UPDATE_CHUNK_DELAY__: JSON.stringify(resolvedLocalDevUpdate?.chunkDelay) ?? "undefined",
|
|
783
|
+
__EIU_LOCAL_DEV_UPDATE_CHUNK_SIZE__: JSON.stringify(resolvedLocalDevUpdate?.chunkSize) ?? "undefined",
|
|
784
|
+
__EIU_LOCAL_DEV_UPDATE_DIR__: JSON.stringify(resolvedLocalDevUpdate?.baseDir ?? ""),
|
|
785
|
+
__EIU_MAIN_FILE__: JSON.stringify(mainFileName),
|
|
786
|
+
__EIU_SIGNATURE_CERT__: JSON.stringify(buildVersionOption.cert),
|
|
787
|
+
__EIU_VERSION_PATH__: JSON.stringify(versionPath)
|
|
893
788
|
};
|
|
894
789
|
const _electronOptions = [{
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
790
|
+
name: "main",
|
|
791
|
+
input: main.files,
|
|
792
|
+
onstart: mainOnstart,
|
|
793
|
+
notBundle,
|
|
794
|
+
plugins: [isESM && esmShim(), bytecodeOptions && bytecodePlugin("main", minify, isESM, bytecodeOptions)],
|
|
795
|
+
options: mergeConfig({
|
|
899
796
|
build: {
|
|
900
797
|
sourcemap,
|
|
901
798
|
minify,
|
|
902
799
|
outDir: `${buildAsarOption.electronDistPath}/main`,
|
|
903
800
|
rolldownOptions: {
|
|
904
801
|
external: finalExternal,
|
|
905
|
-
|
|
906
|
-
|
|
802
|
+
output: {
|
|
803
|
+
format: isESM ? "es" : "cjs",
|
|
804
|
+
polyfillRequire: isESM,
|
|
805
|
+
...outputNames
|
|
806
|
+
}
|
|
907
807
|
}
|
|
908
808
|
},
|
|
909
809
|
define
|
|
910
|
-
},
|
|
810
|
+
}, main.options ?? {})
|
|
911
811
|
}];
|
|
912
|
-
if (
|
|
812
|
+
if (preload?.files) _electronOptions.push({
|
|
813
|
+
name: "preload",
|
|
913
814
|
onstart(args) {
|
|
914
815
|
args.reload();
|
|
915
816
|
},
|
|
916
|
-
|
|
917
|
-
|
|
817
|
+
notBundle,
|
|
818
|
+
input: preload.files,
|
|
819
|
+
plugins: [isESM && esmShim(), bytecodeOptions && bytecodePlugin("preload", minify, isESM, bytecodeOptions)],
|
|
820
|
+
options: mergeConfig({
|
|
918
821
|
build: {
|
|
919
822
|
sourcemap: sourcemap ? "inline" : void 0,
|
|
920
823
|
minify,
|
|
921
824
|
outDir: `${buildAsarOption.electronDistPath}/preload`,
|
|
922
825
|
rolldownOptions: {
|
|
923
826
|
external: finalExternal,
|
|
924
|
-
input: _preload.files,
|
|
925
827
|
output: {
|
|
926
828
|
format: "cjs",
|
|
927
|
-
|
|
829
|
+
codeSplitting: false,
|
|
928
830
|
polyfillRequire: false,
|
|
929
|
-
|
|
930
|
-
chunkFileNames: `[name].${isESM ? "mjs" : "js"}`,
|
|
931
|
-
assetFileNames: "[name].[ext]"
|
|
831
|
+
...outputNames
|
|
932
832
|
}
|
|
933
833
|
}
|
|
934
834
|
},
|
|
935
835
|
define
|
|
936
|
-
},
|
|
836
|
+
}, preload?.options ?? {})
|
|
937
837
|
});
|
|
938
838
|
_electronOptions.push({
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
const nodeModulesPath = path.join(entryOutDir, "node_modules");
|
|
963
|
-
for (const m of modules) {
|
|
964
|
-
const { rootPath } = getPackageInfoSync(m) || {};
|
|
965
|
-
if (!rootPath) {
|
|
966
|
-
log.warn(`Package '${m}' not found`, { timestamp: true });
|
|
967
|
-
continue;
|
|
968
|
-
}
|
|
969
|
-
copyAndSkipIfExist(rootPath, path.join(nodeModulesPath, m), skipIfExist);
|
|
970
|
-
}
|
|
839
|
+
name: "entry",
|
|
840
|
+
input: entry.files,
|
|
841
|
+
async onstart(args) {
|
|
842
|
+
if (mainOnstart) await mainOnstart(args);
|
|
843
|
+
else await args.startup();
|
|
844
|
+
},
|
|
845
|
+
notBundle,
|
|
846
|
+
plugins: [
|
|
847
|
+
isESM && esmShim(),
|
|
848
|
+
bytecodeOptions && bytecodePlugin("entry", minify, isESM, bytecodeOptions),
|
|
849
|
+
{
|
|
850
|
+
name: `${id}:entry`,
|
|
851
|
+
async closeBundle() {
|
|
852
|
+
log.info(`Build entry to '${entryOutDir}'`, { timestamp: true });
|
|
853
|
+
await entry.postBuild?.({
|
|
854
|
+
isBuild: !context.isDev,
|
|
855
|
+
getPathFromEntryOutputDir(...paths) {
|
|
856
|
+
return path.join(entryOutDir, ...paths);
|
|
857
|
+
},
|
|
858
|
+
copyToEntryOutputDir({ from, to = path.basename(from), skipIfExist = true }) {
|
|
859
|
+
if (!fs.existsSync(from)) {
|
|
860
|
+
log.warn(`${from} not found`, { timestamp: true });
|
|
861
|
+
return;
|
|
971
862
|
}
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
else await buildUpdateJson(buildVersionOption, buffer);
|
|
977
|
-
} catch (error) {
|
|
978
|
-
console.error(error);
|
|
863
|
+
copyAndSkipIfExist(from, path.join(entryOutDir, to), skipIfExist);
|
|
864
|
+
},
|
|
865
|
+
copyModules() {
|
|
866
|
+
console.warn("`copyModules()` is deprecated. Will do nothing");
|
|
979
867
|
}
|
|
980
|
-
}
|
|
868
|
+
});
|
|
869
|
+
if (context.isDev) return;
|
|
870
|
+
const buffer = await buildAsar(context.root, buildAsarOption);
|
|
871
|
+
if (!buildVersionJson && !isCI) log.warn("No `buildVersionJson` option setup, skip build version json. Only build in CI by default", { timestamp: true });
|
|
872
|
+
else await buildUpdateJson(buildVersionOption, buffer);
|
|
981
873
|
}
|
|
982
|
-
|
|
874
|
+
}
|
|
875
|
+
],
|
|
876
|
+
options: mergeConfig({
|
|
983
877
|
build: {
|
|
984
878
|
sourcemap,
|
|
985
879
|
minify,
|
|
986
880
|
outDir: entryOutDir,
|
|
987
881
|
rolldownOptions: {
|
|
988
882
|
external: finalExternal,
|
|
989
|
-
|
|
990
|
-
|
|
883
|
+
output: {
|
|
884
|
+
format: isESM ? "es" : "cjs",
|
|
885
|
+
polyfillRequire: isESM,
|
|
886
|
+
...outputNames
|
|
887
|
+
}
|
|
991
888
|
}
|
|
992
889
|
},
|
|
993
890
|
define
|
|
994
|
-
},
|
|
891
|
+
}, entry.options || {})
|
|
892
|
+
});
|
|
893
|
+
return _electronOptions;
|
|
894
|
+
}
|
|
895
|
+
/**
|
|
896
|
+
* Base on `vite-plugin-electron/multi-env`
|
|
897
|
+
* - integrate with updater
|
|
898
|
+
* - no `renderer` config
|
|
899
|
+
* - remove old output file
|
|
900
|
+
* - externalize dependencies
|
|
901
|
+
* - auto restart when entry file changes
|
|
902
|
+
*
|
|
903
|
+
* You can override all the environment configs, except output directories (use `options.updater.paths.electronDistPath` instead)
|
|
904
|
+
*
|
|
905
|
+
* @example
|
|
906
|
+
* ```ts
|
|
907
|
+
* import { defineConfig } from 'vite'
|
|
908
|
+
* import { electronWithUpdater } from 'electron-incremental-update/vite'
|
|
909
|
+
*
|
|
910
|
+
* export default defineConfig(async ({ command }) => {
|
|
911
|
+
* const isBuild = command === 'build'
|
|
912
|
+
* return {
|
|
913
|
+
* plugins: [
|
|
914
|
+
* electronWithUpdater({
|
|
915
|
+
* isBuild,
|
|
916
|
+
* main: {
|
|
917
|
+
* files: ['./electron/main/index.ts', './electron/main/worker.ts'],
|
|
918
|
+
* },
|
|
919
|
+
* preload: {
|
|
920
|
+
* files: './electron/preload/index.ts',
|
|
921
|
+
* },
|
|
922
|
+
* updater: {
|
|
923
|
+
* // options
|
|
924
|
+
* }
|
|
925
|
+
* }),
|
|
926
|
+
* ],
|
|
927
|
+
* }
|
|
928
|
+
* })
|
|
929
|
+
* ```
|
|
930
|
+
*/
|
|
931
|
+
async function electronWithUpdater(options) {
|
|
932
|
+
return electronPluginFactory((context) => {
|
|
933
|
+
process.CACHED_ELECTRON_OPTIONS ??= createElectronOptions(options, context);
|
|
934
|
+
return process.CACHED_ELECTRON_OPTIONS;
|
|
995
935
|
});
|
|
996
|
-
return electron(isESM, normalizePath(path.resolve(root)), _electronOptions);
|
|
997
936
|
}
|
|
998
|
-
|
|
999
937
|
//#endregion
|
|
1000
938
|
//#region src/vite/define.ts
|
|
1001
939
|
/**
|
|
@@ -1008,8 +946,6 @@ async function electronWithUpdater(options) {
|
|
|
1008
946
|
* export default defineElectronConfig({
|
|
1009
947
|
* main: {
|
|
1010
948
|
* files: ['./electron/main/index.ts', './electron/main/worker.ts'],
|
|
1011
|
-
* // see https://github.com/electron-vite/electron-vite-vue/blob/85ed267c4851bf59f32888d766c0071661d4b94c/vite.config.ts#L22-L28
|
|
1012
|
-
* onstart: debugStartup,
|
|
1013
949
|
* },
|
|
1014
950
|
* preload: {
|
|
1015
951
|
* files: './electron/preload/index.ts',
|
|
@@ -1030,21 +966,16 @@ async function electronWithUpdater(options) {
|
|
|
1030
966
|
* ```
|
|
1031
967
|
*/
|
|
1032
968
|
function defineElectronConfig(options) {
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
result.
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
result.build.outDir = rendererDistPath;
|
|
1044
|
-
}
|
|
1045
|
-
return result;
|
|
1046
|
-
};
|
|
969
|
+
const electronPlugin = electronWithUpdater(options);
|
|
970
|
+
const result = options.renderer ?? {};
|
|
971
|
+
result.plugins ??= [];
|
|
972
|
+
result.plugins.push(electronPlugin);
|
|
973
|
+
const rendererDistPath = options.updater?.paths?.rendererDistPath;
|
|
974
|
+
if (rendererDistPath) {
|
|
975
|
+
result.build ??= {};
|
|
976
|
+
result.build.outDir = rendererDistPath;
|
|
977
|
+
}
|
|
978
|
+
return result;
|
|
1047
979
|
}
|
|
1048
|
-
|
|
1049
980
|
//#endregion
|
|
1050
|
-
export {
|
|
981
|
+
export { electronWithUpdater as default, electronWithUpdater, defineElectronConfig, filterErrorMessageStartup, fixWinCharEncoding };
|