electron-incremental-update 1.1.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +162 -25
- package/dist/chunk-7ET4GMTZ.js +236 -0
- package/dist/chunk-HWUYTDEF.js +236 -0
- package/dist/chunk-RQCTJY4L.js +236 -0
- package/dist/chunk-SBPTSLG7.js +235 -0
- package/dist/index.cjs +109 -168
- package/dist/index.d.cts +78 -59
- package/dist/index.d.ts +78 -59
- package/dist/index.js +104 -164
- package/dist/utils.cjs +5 -6
- package/dist/utils.d.cts +4 -4
- package/dist/utils.d.ts +4 -4
- package/dist/utils.js +1 -1
- package/dist/vite.d.ts +32 -6
- package/dist/vite.js +441 -80
- package/package.json +7 -2
package/dist/vite.js
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
// src/vite.ts
|
|
2
2
|
import { basename as basename2, join as join2, resolve } from "node:path";
|
|
3
|
-
import { cpSync, existsSync as existsSync4,
|
|
4
|
-
import {
|
|
3
|
+
import { cpSync, existsSync as existsSync4, rmSync as rmSync2 } from "node:fs";
|
|
4
|
+
import { mergeConfig as mergeConfig2, normalizePath as normalizePath2 } from "vite";
|
|
5
5
|
import ElectronSimple from "vite-plugin-electron/simple";
|
|
6
6
|
import { startup } from "vite-plugin-electron";
|
|
7
7
|
import { notBundle } from "vite-plugin-electron/plugin";
|
|
8
|
+
import { loadPackageJSON } from "local-pkg";
|
|
8
9
|
|
|
9
10
|
// src/build-plugins/build.ts
|
|
10
11
|
import { existsSync as existsSync2, readFileSync as readFileSync2, renameSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
11
|
-
import { join } from "node:path";
|
|
12
|
+
import { basename, join } from "node:path";
|
|
12
13
|
import Asar from "@electron/asar";
|
|
13
14
|
import { build } from "esbuild";
|
|
14
|
-
import "vite";
|
|
15
|
+
import { mergeConfig } from "vite";
|
|
15
16
|
|
|
16
17
|
// src/crypto/utils.ts
|
|
17
18
|
import { createHash } from "node:crypto";
|
|
@@ -35,8 +36,7 @@ var signature = (buffer, privateKey, cert, version) => {
|
|
|
35
36
|
|
|
36
37
|
// src/utils/pure.ts
|
|
37
38
|
function parseVersion(version) {
|
|
38
|
-
const
|
|
39
|
-
const match = semver.exec(version);
|
|
39
|
+
const match = /^(\d+)\.(\d+)\.(\d+)(?:-([a-z0-9.-]+))?/i.exec(version);
|
|
40
40
|
if (!match) {
|
|
41
41
|
throw new TypeError(`invalid version: ${version}`);
|
|
42
42
|
}
|
|
@@ -65,14 +65,14 @@ function isUpdateJSON(json) {
|
|
|
65
65
|
|
|
66
66
|
// src/utils/zip.ts
|
|
67
67
|
import { existsSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
68
|
-
import {
|
|
68
|
+
import { brotliCompress, brotliDecompress } from "node:zlib";
|
|
69
69
|
async function zipFile(filePath, targetFilePath = `${filePath}.gz`) {
|
|
70
70
|
if (!existsSync(filePath)) {
|
|
71
71
|
throw new Error(`path to be zipped not exist: ${filePath}`);
|
|
72
72
|
}
|
|
73
73
|
const buffer = readFileSync(filePath);
|
|
74
74
|
return new Promise((resolve2, reject) => {
|
|
75
|
-
|
|
75
|
+
brotliCompress(buffer, (err, buffer2) => {
|
|
76
76
|
if (err) {
|
|
77
77
|
reject(err);
|
|
78
78
|
}
|
|
@@ -82,6 +82,194 @@ async function zipFile(filePath, targetFilePath = `${filePath}.gz`) {
|
|
|
82
82
|
});
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
+
// src/build-plugins/log.ts
|
|
86
|
+
import { createLogger } from "vite";
|
|
87
|
+
|
|
88
|
+
// src/build-plugins/constant.ts
|
|
89
|
+
var id = "electron-incremental-updater";
|
|
90
|
+
var bytecodeId = `${id}-bytecode`;
|
|
91
|
+
var loaderId = `${id}-loader`;
|
|
92
|
+
|
|
93
|
+
// src/build-plugins/log.ts
|
|
94
|
+
var log = createLogger("info", { prefix: `[${id}]` });
|
|
95
|
+
var bytecodeLog = createLogger("info", { prefix: `[${bytecodeId}]` });
|
|
96
|
+
|
|
97
|
+
// src/build-plugins/bytecode/code.ts
|
|
98
|
+
var 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";
|
|
99
|
+
var bytecodeModuleLoaderCode = [
|
|
100
|
+
`"use strict";`,
|
|
101
|
+
`const fs = require("fs");`,
|
|
102
|
+
`const path = require("path");`,
|
|
103
|
+
`const vm = require("vm");`,
|
|
104
|
+
`const v8 = require("v8");`,
|
|
105
|
+
`const Module = require("module");`,
|
|
106
|
+
`v8.setFlagsFromString("--no-lazy");`,
|
|
107
|
+
`v8.setFlagsFromString("--no-flush-bytecode");`,
|
|
108
|
+
`const FLAG_HASH_OFFSET = 12;`,
|
|
109
|
+
`const SOURCE_HASH_OFFSET = 8;`,
|
|
110
|
+
`let dummyBytecode;`,
|
|
111
|
+
`function setFlagHashHeader(bytecodeBuffer) {`,
|
|
112
|
+
` if (!dummyBytecode) {`,
|
|
113
|
+
` const script = new vm.Script("", {`,
|
|
114
|
+
` produceCachedData: true`,
|
|
115
|
+
` });`,
|
|
116
|
+
` dummyBytecode = script.createCachedData();`,
|
|
117
|
+
` }`,
|
|
118
|
+
` dummyBytecode.slice(FLAG_HASH_OFFSET, FLAG_HASH_OFFSET + 4).copy(bytecodeBuffer, FLAG_HASH_OFFSET);`,
|
|
119
|
+
`};`,
|
|
120
|
+
`function getSourceHashHeader(bytecodeBuffer) {`,
|
|
121
|
+
` return bytecodeBuffer.slice(SOURCE_HASH_OFFSET, SOURCE_HASH_OFFSET + 4);`,
|
|
122
|
+
`};`,
|
|
123
|
+
`function buffer2Number(buffer) {`,
|
|
124
|
+
` let ret = 0;`,
|
|
125
|
+
` ret |= buffer[3] << 24;`,
|
|
126
|
+
` ret |= buffer[2] << 16;`,
|
|
127
|
+
` ret |= buffer[1] << 8;`,
|
|
128
|
+
` ret |= buffer[0];`,
|
|
129
|
+
` return ret;`,
|
|
130
|
+
`};`,
|
|
131
|
+
`Module._extensions[".jsc"] = Module._extensions[".cjsc"] = function (module, filename) {`,
|
|
132
|
+
` const bytecodeBuffer = fs.readFileSync(filename);`,
|
|
133
|
+
` if (!Buffer.isBuffer(bytecodeBuffer)) {`,
|
|
134
|
+
` throw new Error("BytecodeBuffer must be a buffer object.");`,
|
|
135
|
+
` }`,
|
|
136
|
+
` setFlagHashHeader(bytecodeBuffer);`,
|
|
137
|
+
` const length = buffer2Number(getSourceHashHeader(bytecodeBuffer));`,
|
|
138
|
+
` let dummyCode = "";`,
|
|
139
|
+
` if (length > 1) {`,
|
|
140
|
+
` dummyCode = "\\"" + "\\u200b".repeat(length - 2) + "\\"";`,
|
|
141
|
+
` }`,
|
|
142
|
+
` const script = new vm.Script(dummyCode, {`,
|
|
143
|
+
` filename: filename,`,
|
|
144
|
+
` lineOffset: 0,`,
|
|
145
|
+
` displayErrors: true,`,
|
|
146
|
+
` cachedData: bytecodeBuffer`,
|
|
147
|
+
` });`,
|
|
148
|
+
` if (script.cachedDataRejected) {`,
|
|
149
|
+
` throw new Error("Invalid or incompatible cached data (cachedDataRejected)");`,
|
|
150
|
+
` }`,
|
|
151
|
+
` const require = function (id) {`,
|
|
152
|
+
` return module.require(id);`,
|
|
153
|
+
` };`,
|
|
154
|
+
` require.resolve = function (request, options) {`,
|
|
155
|
+
` return Module._resolveFilename(request, module, false, options);`,
|
|
156
|
+
` };`,
|
|
157
|
+
` if (process.mainModule) {`,
|
|
158
|
+
` require.main = process.mainModule;`,
|
|
159
|
+
` }`,
|
|
160
|
+
` require.extensions = Module._extensions;`,
|
|
161
|
+
` require.cache = Module._cache;`,
|
|
162
|
+
` const compiledWrapper = script.runInThisContext({`,
|
|
163
|
+
` filename: filename,`,
|
|
164
|
+
` lineOffset: 0,`,
|
|
165
|
+
` columnOffset: 0,`,
|
|
166
|
+
` displayErrors: true`,
|
|
167
|
+
` });`,
|
|
168
|
+
` const dirname = path.dirname(filename);`,
|
|
169
|
+
` const args = [module.exports, require, module, filename, dirname, process, global];`,
|
|
170
|
+
` return compiledWrapper.apply(module.exports, args);`,
|
|
171
|
+
`};`
|
|
172
|
+
].join("\n");
|
|
173
|
+
|
|
174
|
+
// src/build-plugins/bytecode/utils.ts
|
|
175
|
+
import path from "node:path";
|
|
176
|
+
import fs from "node:fs";
|
|
177
|
+
import { spawn } from "node:child_process";
|
|
178
|
+
import * as babel from "@babel/core";
|
|
179
|
+
import MagicString from "magic-string";
|
|
180
|
+
import { getPackageInfoSync } from "local-pkg";
|
|
181
|
+
var electronModulePath = getPackageInfoSync("electron")?.rootPath;
|
|
182
|
+
var useStrict = "'use strict';";
|
|
183
|
+
var bytecodeModuleLoader = "__loader__.js";
|
|
184
|
+
function getElectronPath() {
|
|
185
|
+
let electronExecPath = process.env.ELECTRON_EXEC_PATH || "";
|
|
186
|
+
if (!electronExecPath) {
|
|
187
|
+
if (!electronModulePath) {
|
|
188
|
+
throw new Error("Electron is not installed");
|
|
189
|
+
}
|
|
190
|
+
const pathFile = path.join(electronModulePath, "path.txt");
|
|
191
|
+
let executablePath;
|
|
192
|
+
if (fs.existsSync(pathFile)) {
|
|
193
|
+
executablePath = fs.readFileSync(pathFile, "utf-8");
|
|
194
|
+
}
|
|
195
|
+
if (executablePath) {
|
|
196
|
+
electronExecPath = path.join(electronModulePath, "dist", executablePath);
|
|
197
|
+
process.env.ELECTRON_EXEC_PATH = electronExecPath;
|
|
198
|
+
} else {
|
|
199
|
+
throw new Error("Electron executable file is not existed");
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return electronExecPath;
|
|
203
|
+
}
|
|
204
|
+
function getBytecodeCompilerPath() {
|
|
205
|
+
const scriptPath = path.join(electronModulePath, "bytenode.cjs");
|
|
206
|
+
if (!fs.existsSync(scriptPath)) {
|
|
207
|
+
fs.writeFileSync(scriptPath, bytecodeGeneratorScript);
|
|
208
|
+
}
|
|
209
|
+
return scriptPath;
|
|
210
|
+
}
|
|
211
|
+
function toRelativePath(filename, importer) {
|
|
212
|
+
const relPath = path.posix.relative(path.dirname(importer), filename);
|
|
213
|
+
return relPath.startsWith(".") ? relPath : `./${relPath}`;
|
|
214
|
+
}
|
|
215
|
+
function compileToBytecode(code) {
|
|
216
|
+
let data = Buffer.from([]);
|
|
217
|
+
const logErr = (...args) => log.error(args.join(" "), { timestamp: true });
|
|
218
|
+
const electronPath = getElectronPath();
|
|
219
|
+
const bytecodePath = getBytecodeCompilerPath();
|
|
220
|
+
return new Promise((resolve2, reject) => {
|
|
221
|
+
const proc = spawn(electronPath, [bytecodePath], {
|
|
222
|
+
env: { ELECTRON_RUN_AS_NODE: "1" },
|
|
223
|
+
stdio: ["pipe", "pipe", "pipe", "ipc"]
|
|
224
|
+
});
|
|
225
|
+
if (proc.stdin) {
|
|
226
|
+
proc.stdin.write(code);
|
|
227
|
+
proc.stdin.end();
|
|
228
|
+
}
|
|
229
|
+
if (proc.stdout) {
|
|
230
|
+
proc.stdout.on("data", (chunk) => data = Buffer.concat([data, chunk]));
|
|
231
|
+
proc.stdout.on("error", (err) => logErr(err));
|
|
232
|
+
proc.stdout.on("end", () => resolve2(data));
|
|
233
|
+
}
|
|
234
|
+
if (proc.stderr) {
|
|
235
|
+
proc.stderr.on("data", (chunk) => logErr("Error: ", chunk.toString()));
|
|
236
|
+
proc.stderr.on("error", (err) => logErr("Error: ", err));
|
|
237
|
+
}
|
|
238
|
+
proc.addListener("error", (err) => logErr(err));
|
|
239
|
+
proc.on("error", (err) => reject(err));
|
|
240
|
+
proc.on("exit", () => resolve2(data));
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
function convertArrowToFunction(code) {
|
|
244
|
+
const result = babel.transform(code, {
|
|
245
|
+
plugins: ["@babel/plugin-transform-arrow-functions"]
|
|
246
|
+
});
|
|
247
|
+
return {
|
|
248
|
+
code: result?.code || code,
|
|
249
|
+
map: result?.map
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
function escapeRegExpString(str) {
|
|
253
|
+
return str.replace(/\\/g, "\\\\").replace(/[|{}()[\]^$+*?.]/g, "\\$&");
|
|
254
|
+
}
|
|
255
|
+
function convertString(code, strings, sourcemap) {
|
|
256
|
+
let s = null;
|
|
257
|
+
for (const str of strings.filter(Boolean)) {
|
|
258
|
+
const regex = new RegExp(`["']${escapeRegExpString(str)}["']`, "g");
|
|
259
|
+
s ||= new MagicString(code).replace(regex, (match) => obfuscateString(match.slice(1, -1)));
|
|
260
|
+
}
|
|
261
|
+
return s ? {
|
|
262
|
+
code: s.toString(),
|
|
263
|
+
map: sourcemap ? s.generateMap({ hires: "boundary" }) : null
|
|
264
|
+
} : { code };
|
|
265
|
+
}
|
|
266
|
+
function obfuscateString(input) {
|
|
267
|
+
const offset = Math.floor(Math.random() * 2 << 4) + 1;
|
|
268
|
+
const hexArray = Array.from(input).map((c) => "0x" + (c.charCodeAt(0) + offset).toString(16));
|
|
269
|
+
const decodeFn = `function(a,b){return String.fromCharCode.apply(null,a.map(x=>+x-b))}`;
|
|
270
|
+
return `(${decodeFn})([${hexArray.join(",")}],${offset})`;
|
|
271
|
+
}
|
|
272
|
+
|
|
85
273
|
// src/build-plugins/build.ts
|
|
86
274
|
async function buildAsar({
|
|
87
275
|
version,
|
|
@@ -158,25 +346,66 @@ async function buildEntry({
|
|
|
158
346
|
entryOutputDirPath,
|
|
159
347
|
nativeModuleEntryMap,
|
|
160
348
|
overrideEsbuildOptions
|
|
161
|
-
}) {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
"
|
|
349
|
+
}, cert, protectedStrings) {
|
|
350
|
+
const option = mergeConfig(
|
|
351
|
+
{
|
|
352
|
+
entryPoints: {
|
|
353
|
+
entry: appEntryPath,
|
|
354
|
+
...nativeModuleEntryMap
|
|
355
|
+
},
|
|
356
|
+
bundle: true,
|
|
357
|
+
metafile: true,
|
|
358
|
+
platform: "node",
|
|
359
|
+
outdir: entryOutputDirPath,
|
|
360
|
+
minify,
|
|
361
|
+
sourcemap,
|
|
362
|
+
entryNames: "[dir]/[name]",
|
|
363
|
+
assetNames: "[dir]/[name]",
|
|
364
|
+
external: ["electron", "original-fs"],
|
|
365
|
+
loader: {
|
|
366
|
+
".node": "empty"
|
|
367
|
+
},
|
|
368
|
+
define: {
|
|
369
|
+
__SIGNATURE_CERT__: JSON.stringify(cert)
|
|
370
|
+
}
|
|
177
371
|
},
|
|
178
|
-
|
|
179
|
-
|
|
372
|
+
overrideEsbuildOptions ?? {}
|
|
373
|
+
);
|
|
374
|
+
const { metafile } = await build(option);
|
|
375
|
+
if (protectedStrings === void 0) {
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
const filePaths = Object.keys(metafile?.outputs ?? []);
|
|
379
|
+
for (const filePath of filePaths) {
|
|
380
|
+
let code = readFileSync2(filePath, "utf-8");
|
|
381
|
+
const fileName = basename(filePath);
|
|
382
|
+
const isEntry = fileName.endsWith("entry.js");
|
|
383
|
+
if (isEntry) {
|
|
384
|
+
code = code.replace(
|
|
385
|
+
/(`-----BEGIN CERTIFICATE-----[\s\S]*-----END CERTIFICATE-----\n`)/,
|
|
386
|
+
(_, cert2) => `"${cert2.slice(1, -1).replace(/\n/g, "\\n")}"`
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
const transformedCode = convertString(
|
|
390
|
+
convertArrowToFunction(code).code,
|
|
391
|
+
[...protectedStrings, ...isEntry ? getCert(code) : []]
|
|
392
|
+
).code;
|
|
393
|
+
const buffer = await compileToBytecode(transformedCode);
|
|
394
|
+
writeFileSync2(`${filePath}c`, buffer);
|
|
395
|
+
writeFileSync2(
|
|
396
|
+
filePath,
|
|
397
|
+
`${isEntry ? bytecodeModuleLoaderCode : useStrict}${isEntry ? "" : "module.exports = "}require("./${fileName}c")`
|
|
398
|
+
);
|
|
399
|
+
bytecodeLog.info(
|
|
400
|
+
`${filePath} => ${(buffer.byteLength / 1e3).toFixed(2)} kB`,
|
|
401
|
+
{ timestamp: true }
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
bytecodeLog.info(`${filePaths.length} bundles compiled into bytecode`, { timestamp: true });
|
|
405
|
+
}
|
|
406
|
+
function getCert(code) {
|
|
407
|
+
const cert = code.match(/-----BEGIN CERTIFICATE-----[\s\S]*-----END CERTIFICATE-----\\n/)?.[0];
|
|
408
|
+
return cert ? [cert] : [];
|
|
180
409
|
}
|
|
181
410
|
|
|
182
411
|
// src/build-plugins/key.ts
|
|
@@ -200,29 +429,10 @@ function generateKeyPair(keyLength, subject, days, privateKeyPath, certPath) {
|
|
|
200
429
|
writeFileSync3(privateKeyPath, privateKey.replace(/\r\n?/g, "\n"));
|
|
201
430
|
writeFileSync3(certPath, cert.replace(/\r\n?/g, "\n"));
|
|
202
431
|
}
|
|
203
|
-
var noCertRegex = /(?<=const SIGNATURE_CERT\s*=\s*)['"]{2}/;
|
|
204
|
-
var existCertRegex = /(?<=const SIGNATURE_CERT\s*=\s*)(['"]-----BEGIN CERTIFICATE-----[\s\S]*-----END CERTIFICATE-----\\n['"])/;
|
|
205
|
-
function writeCertToEntry(entryPath, cert) {
|
|
206
|
-
if (!existsSync3(entryPath)) {
|
|
207
|
-
throw new Error(`entry not exist: ${entryPath}`);
|
|
208
|
-
}
|
|
209
|
-
const file = readFileSync3(entryPath, "utf-8");
|
|
210
|
-
const replacement = cert.split("\n").filter(Boolean).map((s) => `'${s}\\n'`).join("\n + ");
|
|
211
|
-
let replaced = file;
|
|
212
|
-
if (noCertRegex.test(file)) {
|
|
213
|
-
replaced = file.replace(noCertRegex, replacement);
|
|
214
|
-
} else if (existCertRegex.test(file)) {
|
|
215
|
-
replaced = file.replace(existCertRegex, replacement);
|
|
216
|
-
} else {
|
|
217
|
-
throw new Error("no `SIGNATURE_CERT` found in entry");
|
|
218
|
-
}
|
|
219
|
-
writeFileSync3(entryPath, replaced);
|
|
220
|
-
}
|
|
221
432
|
function parseKeys({
|
|
222
433
|
keyLength,
|
|
223
434
|
privateKeyPath,
|
|
224
435
|
certPath,
|
|
225
|
-
appEntryPath,
|
|
226
436
|
subject,
|
|
227
437
|
days
|
|
228
438
|
}) {
|
|
@@ -234,7 +444,6 @@ function parseKeys({
|
|
|
234
444
|
}
|
|
235
445
|
const privateKey = process.env.UPDATER_PK || readFileSync3(privateKeyPath, "utf-8");
|
|
236
446
|
const cert = process.env.UPDATER_CERT || readFileSync3(certPath, "utf-8");
|
|
237
|
-
writeCertToEntry(appEntryPath, cert);
|
|
238
447
|
return { privateKey, cert };
|
|
239
448
|
}
|
|
240
449
|
function parseSubjects(subject) {
|
|
@@ -296,7 +505,6 @@ function parseOptions(pkg, sourcemap = false, minify = false, options = {}) {
|
|
|
296
505
|
keyLength,
|
|
297
506
|
privateKeyPath,
|
|
298
507
|
certPath,
|
|
299
|
-
appEntryPath,
|
|
300
508
|
subject,
|
|
301
509
|
days
|
|
302
510
|
});
|
|
@@ -310,37 +518,176 @@ function parseOptions(pkg, sourcemap = false, minify = false, options = {}) {
|
|
|
310
518
|
generateSignature,
|
|
311
519
|
generateVersionJson
|
|
312
520
|
};
|
|
313
|
-
return { buildAsarOption, buildEntryOption, buildVersionOption, postBuild };
|
|
521
|
+
return { buildAsarOption, buildEntryOption, buildVersionOption, postBuild, cert };
|
|
314
522
|
}
|
|
315
523
|
|
|
316
|
-
// src/
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
524
|
+
// src/build-plugins/bytecode/index.ts
|
|
525
|
+
import path2 from "node:path";
|
|
526
|
+
import fs2 from "node:fs";
|
|
527
|
+
import { createFilter, normalizePath } from "vite";
|
|
528
|
+
import MagicString2 from "magic-string";
|
|
529
|
+
function bytecodePlugin(isBuild, env, options = {}) {
|
|
530
|
+
if (!isBuild) {
|
|
531
|
+
return null;
|
|
532
|
+
}
|
|
533
|
+
const {
|
|
534
|
+
protectedStrings = [],
|
|
535
|
+
enablePreload = false
|
|
536
|
+
} = options;
|
|
537
|
+
if (!enablePreload && env === "preload") {
|
|
538
|
+
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 });
|
|
539
|
+
return null;
|
|
540
|
+
}
|
|
541
|
+
const filter = createFilter(/\.(m?[jt]s|[jt]sx)$/);
|
|
542
|
+
let config;
|
|
543
|
+
let bytecodeRequired = false;
|
|
544
|
+
let bytecodeFiles = [];
|
|
545
|
+
return {
|
|
546
|
+
name: `${bytecodeId}-${env}`,
|
|
547
|
+
apply: "build",
|
|
548
|
+
enforce: "post",
|
|
549
|
+
configResolved(resolvedConfig) {
|
|
550
|
+
config = resolvedConfig;
|
|
551
|
+
},
|
|
552
|
+
transform(code, id2) {
|
|
553
|
+
if (protectedStrings.length === 0 || !filter(id2)) {
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
return convertString(code, protectedStrings, !!config.build.sourcemap);
|
|
557
|
+
},
|
|
558
|
+
generateBundle(options2) {
|
|
559
|
+
if (options2.format !== "es" && bytecodeRequired) {
|
|
560
|
+
this.emitFile({
|
|
561
|
+
type: "asset",
|
|
562
|
+
source: bytecodeModuleLoaderCode + "\n",
|
|
563
|
+
name: "Bytecode Loader File",
|
|
564
|
+
fileName: bytecodeModuleLoader
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
},
|
|
568
|
+
renderChunk(code, chunk, options2) {
|
|
569
|
+
if (options2.format === "es") {
|
|
570
|
+
bytecodeLog.warn(
|
|
571
|
+
'bytecodePlugin does not support ES module, please remove "type": "module" in package.json or set the "build.rollupOptions.output.format" option to "cjs".',
|
|
572
|
+
{ timestamp: true }
|
|
573
|
+
);
|
|
574
|
+
return null;
|
|
575
|
+
}
|
|
576
|
+
if (chunk.type === "chunk") {
|
|
577
|
+
bytecodeRequired = true;
|
|
578
|
+
return convertArrowToFunction(code);
|
|
579
|
+
}
|
|
580
|
+
return null;
|
|
581
|
+
},
|
|
582
|
+
async writeBundle(options2, output) {
|
|
583
|
+
if (options2.format === "es" || !bytecodeRequired) {
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
const outDir = options2.dir;
|
|
587
|
+
bytecodeFiles = [];
|
|
588
|
+
const bundles = Object.keys(output);
|
|
589
|
+
const chunks = Object.values(output).filter(
|
|
590
|
+
(chunk) => chunk.type === "chunk" && chunk.fileName !== bytecodeModuleLoader
|
|
591
|
+
);
|
|
592
|
+
const bytecodeChunks = chunks.map((chunk) => chunk.fileName);
|
|
593
|
+
const nonEntryChunks = chunks.filter((chunk) => !chunk.isEntry).map((chunk) => path2.basename(chunk.fileName));
|
|
594
|
+
const pattern = nonEntryChunks.map((chunk) => `(${chunk})`).join("|");
|
|
595
|
+
const bytecodeRE = pattern ? new RegExp(`require\\(\\S*(?=(${pattern})\\S*\\))`, "g") : null;
|
|
596
|
+
const getBytecodeLoaderBlock = (chunkFileName) => {
|
|
597
|
+
return `require("${toRelativePath(bytecodeModuleLoader, normalizePath(chunkFileName))}");`;
|
|
598
|
+
};
|
|
599
|
+
await Promise.all(
|
|
600
|
+
bundles.map(async (name) => {
|
|
601
|
+
const chunk = output[name];
|
|
602
|
+
if (chunk.type === "chunk") {
|
|
603
|
+
let _code = chunk.code;
|
|
604
|
+
if (bytecodeRE && _code.match(bytecodeRE)) {
|
|
605
|
+
let match;
|
|
606
|
+
const s = new MagicString2(_code);
|
|
607
|
+
while (match = bytecodeRE.exec(_code)) {
|
|
608
|
+
const [prefix, chunkName] = match;
|
|
609
|
+
const len = prefix.length + chunkName.length;
|
|
610
|
+
s.overwrite(match.index, match.index + len, prefix + chunkName + "c", {
|
|
611
|
+
contentOnly: true
|
|
612
|
+
});
|
|
613
|
+
}
|
|
614
|
+
_code = s.toString();
|
|
615
|
+
}
|
|
616
|
+
const chunkFilePath = path2.resolve(outDir, name);
|
|
617
|
+
if (bytecodeChunks.includes(name)) {
|
|
618
|
+
const bytecodeBuffer = await compileToBytecode(_code);
|
|
619
|
+
fs2.writeFileSync(path2.resolve(outDir, name + "c"), bytecodeBuffer);
|
|
620
|
+
if (chunk.isEntry) {
|
|
621
|
+
const bytecodeLoaderBlock = getBytecodeLoaderBlock(chunk.fileName);
|
|
622
|
+
const bytecodeModuleBlock = `require("./${path2.basename(name) + "c"}");`;
|
|
623
|
+
const code = `${useStrict}
|
|
624
|
+
${bytecodeLoaderBlock}
|
|
625
|
+
module.exports=${bytecodeModuleBlock}
|
|
626
|
+
`;
|
|
627
|
+
fs2.writeFileSync(chunkFilePath, code);
|
|
628
|
+
} else {
|
|
629
|
+
fs2.unlinkSync(chunkFilePath);
|
|
630
|
+
}
|
|
631
|
+
bytecodeFiles.push({ name: name + "c", size: bytecodeBuffer.length });
|
|
632
|
+
} else {
|
|
633
|
+
if (chunk.isEntry) {
|
|
634
|
+
let hasBytecodeMoudle = false;
|
|
635
|
+
const idsToHandle = /* @__PURE__ */ new Set([...chunk.imports, ...chunk.dynamicImports]);
|
|
636
|
+
for (const moduleId of idsToHandle) {
|
|
637
|
+
if (bytecodeChunks.includes(moduleId)) {
|
|
638
|
+
hasBytecodeMoudle = true;
|
|
639
|
+
break;
|
|
640
|
+
}
|
|
641
|
+
const moduleInfo = this.getModuleInfo(moduleId);
|
|
642
|
+
if (moduleInfo && !moduleInfo.isExternal) {
|
|
643
|
+
const { importers, dynamicImporters } = moduleInfo;
|
|
644
|
+
for (const importerId of importers) {
|
|
645
|
+
idsToHandle.add(importerId);
|
|
646
|
+
}
|
|
647
|
+
for (const importerId of dynamicImporters) {
|
|
648
|
+
idsToHandle.add(importerId);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
const bytecodeLoaderBlock = getBytecodeLoaderBlock(chunk.fileName);
|
|
653
|
+
_code = hasBytecodeMoudle ? _code.replace(useStrict, `${useStrict}
|
|
654
|
+
${bytecodeLoaderBlock}`) : _code;
|
|
655
|
+
}
|
|
656
|
+
fs2.writeFileSync(chunkFilePath, _code);
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
})
|
|
660
|
+
);
|
|
661
|
+
},
|
|
662
|
+
closeBundle() {
|
|
663
|
+
const outDir = `${normalizePath(path2.relative(config.root, path2.resolve(config.root, config.build.outDir)))}/`;
|
|
664
|
+
bytecodeFiles.forEach((file) => {
|
|
665
|
+
const kbs = file.size / 1e3;
|
|
666
|
+
bytecodeLog.info(
|
|
667
|
+
`${outDir}${file.name} => ${kbs.toFixed(2)} kB`,
|
|
668
|
+
{ timestamp: true }
|
|
669
|
+
);
|
|
670
|
+
});
|
|
671
|
+
bytecodeLog.info(`${bytecodeFiles.length} bundles compiled into bytecode.`, { timestamp: true });
|
|
672
|
+
bytecodeFiles = [];
|
|
673
|
+
}
|
|
674
|
+
};
|
|
675
|
+
}
|
|
320
676
|
|
|
321
677
|
// src/vite.ts
|
|
322
678
|
function debugStartup(args) {
|
|
323
679
|
process.env.VSCODE_DEBUG ? console.log("[startup] Electron App") : args.startup();
|
|
324
680
|
}
|
|
325
|
-
function
|
|
326
|
-
const packageJsonPath = join2(root, "package.json");
|
|
327
|
-
const packageJsonStr = readFileSync4(packageJsonPath, "utf8");
|
|
328
|
-
try {
|
|
329
|
-
return JSON.parse(packageJsonStr);
|
|
330
|
-
} catch {
|
|
331
|
-
return null;
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
var log = createLogger("info", { prefix: `[${id}]` });
|
|
335
|
-
function electronWithUpdater(options) {
|
|
681
|
+
async function electronWithUpdater(options) {
|
|
336
682
|
let {
|
|
337
683
|
isBuild,
|
|
338
|
-
pkg =
|
|
684
|
+
pkg = await loadPackageJSON(),
|
|
339
685
|
main: _main,
|
|
340
686
|
preload: _preload,
|
|
341
|
-
sourcemap,
|
|
342
|
-
minify,
|
|
687
|
+
sourcemap = !isBuild,
|
|
688
|
+
minify = isBuild,
|
|
343
689
|
updater,
|
|
690
|
+
bytecode,
|
|
344
691
|
useNotBundle = true,
|
|
345
692
|
logParsedOptions
|
|
346
693
|
} = options;
|
|
@@ -348,14 +695,22 @@ function electronWithUpdater(options) {
|
|
|
348
695
|
log.error(`package.json not found`, { timestamp: true });
|
|
349
696
|
return null;
|
|
350
697
|
}
|
|
698
|
+
if (!pkg.version || !pkg.name || !pkg.main) {
|
|
699
|
+
log.error(`package.json not valid`, { timestamp: true });
|
|
700
|
+
return null;
|
|
701
|
+
}
|
|
351
702
|
const _options = parseOptions(pkg, sourcemap, minify, updater);
|
|
703
|
+
const bytecodeOptions = typeof bytecode === "object" ? bytecode : bytecode === true ? { protectedStrings: [] } : void 0;
|
|
704
|
+
if (bytecodeOptions) {
|
|
705
|
+
minify = false;
|
|
706
|
+
}
|
|
352
707
|
try {
|
|
353
708
|
rmSync2(_options.buildAsarOption.electronDistPath, { recursive: true, force: true });
|
|
354
709
|
rmSync2(_options.buildEntryOption.entryOutputDirPath, { recursive: true, force: true });
|
|
355
710
|
} catch (ignore) {
|
|
356
711
|
}
|
|
357
712
|
log.info(`remove old files`, { timestamp: true });
|
|
358
|
-
const { buildAsarOption, buildEntryOption, buildVersionOption, postBuild } = _options;
|
|
713
|
+
const { buildAsarOption, buildEntryOption, buildVersionOption, postBuild, cert } = _options;
|
|
359
714
|
const { entryOutputDirPath, nativeModuleEntryMap, appEntryPath } = buildEntryOption;
|
|
360
715
|
sourcemap ??= isBuild || !!process.env.VSCODE_DEBUG;
|
|
361
716
|
const _appPath = normalizePath2(join2(entryOutputDirPath, "entry.js"));
|
|
@@ -363,7 +718,7 @@ function electronWithUpdater(options) {
|
|
|
363
718
|
throw new Error(`wrong "main" field in package.json: "${pkg.main}", it should be "${_appPath}"`);
|
|
364
719
|
}
|
|
365
720
|
const _buildEntry = async () => {
|
|
366
|
-
await buildEntry(buildEntryOption);
|
|
721
|
+
await buildEntry(buildEntryOption, cert, isBuild ? bytecodeOptions?.protectedStrings : void 0);
|
|
367
722
|
log.info(`vite build entry to '${entryOutputDirPath}'`, { timestamp: true });
|
|
368
723
|
};
|
|
369
724
|
const _postBuild = postBuild ? async () => await postBuild({
|
|
@@ -385,6 +740,13 @@ function electronWithUpdater(options) {
|
|
|
385
740
|
}) : async () => {
|
|
386
741
|
};
|
|
387
742
|
let isInit = false;
|
|
743
|
+
const rollupOptions = {
|
|
744
|
+
// external: [
|
|
745
|
+
// /^node:/,
|
|
746
|
+
// ...Object.keys('dependencies' in pkg ? pkg.dependencies as object : {}),
|
|
747
|
+
// ],
|
|
748
|
+
external: (src) => src.startsWith("node:") || Object.keys("dependencies" in pkg ? pkg.dependencies : {}).includes(src)
|
|
749
|
+
};
|
|
388
750
|
const electronPluginOptions = {
|
|
389
751
|
main: {
|
|
390
752
|
entry: _main.files,
|
|
@@ -396,16 +758,17 @@ function electronWithUpdater(options) {
|
|
|
396
758
|
}
|
|
397
759
|
_main.onstart ? _main.onstart(args) : args.startup();
|
|
398
760
|
},
|
|
399
|
-
vite:
|
|
761
|
+
vite: mergeConfig2(
|
|
400
762
|
{
|
|
401
|
-
plugins: [
|
|
763
|
+
plugins: [
|
|
764
|
+
!isBuild && useNotBundle ? notBundle() : void 0,
|
|
765
|
+
bytecodeOptions && bytecodePlugin(isBuild, "main", bytecodeOptions)
|
|
766
|
+
],
|
|
402
767
|
build: {
|
|
403
768
|
sourcemap,
|
|
404
769
|
minify,
|
|
405
770
|
outDir: `${buildAsarOption.electronDistPath}/main`,
|
|
406
|
-
rollupOptions
|
|
407
|
-
external: Object.keys("dependencies" in pkg ? pkg.dependencies : {})
|
|
408
|
-
}
|
|
771
|
+
rollupOptions
|
|
409
772
|
}
|
|
410
773
|
},
|
|
411
774
|
_main.vite ?? {}
|
|
@@ -414,9 +777,10 @@ function electronWithUpdater(options) {
|
|
|
414
777
|
preload: {
|
|
415
778
|
input: _preload.files,
|
|
416
779
|
onstart: _preload.onstart,
|
|
417
|
-
vite:
|
|
780
|
+
vite: mergeConfig2(
|
|
418
781
|
{
|
|
419
782
|
plugins: [
|
|
783
|
+
bytecodeOptions && bytecodePlugin(isBuild, "preload", bytecodeOptions),
|
|
420
784
|
{
|
|
421
785
|
name: `${id}-build`,
|
|
422
786
|
enforce: "post",
|
|
@@ -437,9 +801,7 @@ function electronWithUpdater(options) {
|
|
|
437
801
|
sourcemap: sourcemap ? "inline" : void 0,
|
|
438
802
|
minify,
|
|
439
803
|
outDir: `${buildAsarOption.electronDistPath}/preload`,
|
|
440
|
-
rollupOptions
|
|
441
|
-
external: Object.keys("dependencies" in pkg ? pkg.dependencies : {})
|
|
442
|
-
}
|
|
804
|
+
rollupOptions
|
|
443
805
|
}
|
|
444
806
|
},
|
|
445
807
|
_preload.vite ?? {}
|
|
@@ -452,7 +814,7 @@ function electronWithUpdater(options) {
|
|
|
452
814
|
...electronPluginOptions,
|
|
453
815
|
updater: { buildAsarOption, buildEntryOption, buildVersionOption }
|
|
454
816
|
},
|
|
455
|
-
(key, value) => key === "privateKey" || key === "cert" ? "***" : value,
|
|
817
|
+
(key, value) => (key === "privateKey" || key === "cert") && !(typeof logParsedOptions === "object" && logParsedOptions.showKeys === true) ? "***" : value,
|
|
456
818
|
2
|
|
457
819
|
),
|
|
458
820
|
{ timestamp: true }
|
|
@@ -481,6 +843,5 @@ function electronWithUpdater(options) {
|
|
|
481
843
|
}
|
|
482
844
|
export {
|
|
483
845
|
debugStartup,
|
|
484
|
-
electronWithUpdater
|
|
485
|
-
log
|
|
846
|
+
electronWithUpdater
|
|
486
847
|
};
|