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