electron-incremental-update 2.0.0-beta.1 → 2.0.0-beta.10

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/dist/vite.js CHANGED
@@ -1,18 +1,22 @@
1
- // src/vite.ts
2
- import { basename as basename2, join as join2, resolve } from "node:path";
3
- import { cpSync, existsSync as existsSync4, rmSync } from "node:fs";
4
- import { mergeConfig as mergeConfig2, normalizePath as normalizePath2 } from "vite";
5
- import ElectronSimple from "vite-plugin-electron/simple";
6
- import { startup } from "vite-plugin-electron";
7
- import { notBundle } from "vite-plugin-electron/plugin";
8
- import { loadPackageJSON } from "local-pkg";
1
+ import path5 from 'node:path';
2
+ import fs3 from 'node:fs';
3
+ import { createLogger, normalizePath, mergeConfig, createFilter } from 'vite';
4
+ import ElectronSimple from 'vite-plugin-electron/simple';
5
+ import { startup } from 'vite-plugin-electron';
6
+ import { notBundle } from 'vite-plugin-electron/plugin';
7
+ import { getPackageInfoSync, loadPackageJSON } from 'local-pkg';
8
+ import { isCI } from 'ci-info';
9
+ export { isCI } from 'ci-info';
10
+ import Asar from '@electron/asar';
11
+ import { build } from 'esbuild';
12
+ import cp from 'node:child_process';
13
+ import * as babel from '@babel/core';
14
+ import MagicString from 'magic-string';
15
+ import zlib from 'node:zlib';
16
+ import crypto from 'node:crypto';
17
+ import { generate } from 'selfsigned';
9
18
 
10
- // src/build-plugins/build.ts
11
- import { existsSync as existsSync2, readFileSync as readFileSync2, renameSync, writeFileSync as writeFileSync2 } from "node:fs";
12
- import { basename, join } from "node:path";
13
- import Asar from "@electron/asar";
14
- import { build } from "esbuild";
15
- import { mergeConfig } from "vite";
19
+ // src/vite/index.ts
16
20
 
17
21
  // src/utils/version.ts
18
22
  function parseVersion(version) {
@@ -39,67 +43,30 @@ function parseVersion(version) {
39
43
  return ret;
40
44
  }
41
45
  function isUpdateJSON(json) {
42
- const is = (j) => !!(j && j.minimumVersion && j.signature && j.size && j.version);
46
+ const is = (j) => !!(j && j.minimumVersion && j.signature && j.version);
43
47
  return is(json) && is(json?.beta);
44
48
  }
45
-
46
- // src/utils/zip.ts
47
- import { existsSync, readFileSync, writeFileSync } from "node:fs";
48
- import { brotliCompress } from "node:zlib";
49
- async function zipFile(filePath, targetFilePath = `${filePath}.gz`) {
50
- if (!existsSync(filePath)) {
51
- throw new Error(`path to be zipped not exist: ${filePath}`);
49
+ function defaultVersionJsonGenerator(existingJson, signature, version, minimumVersion) {
50
+ existingJson.beta = {
51
+ version,
52
+ minimumVersion,
53
+ signature
54
+ };
55
+ if (!parseVersion(version).stage) {
56
+ existingJson.version = version;
57
+ existingJson.minimumVersion = minimumVersion;
58
+ existingJson.signature = signature;
52
59
  }
53
- const buffer = readFileSync(filePath);
54
- return new Promise((resolve2, reject) => {
55
- brotliCompress(buffer, (err, buffer2) => {
56
- if (err) {
57
- reject(err);
58
- }
59
- writeFileSync(targetFilePath, buffer2);
60
- resolve2(null);
61
- });
62
- });
63
- }
64
-
65
- // src/utils/crypto/utils.ts
66
- import { createHash } from "node:crypto";
67
- function hashString(data, length) {
68
- const hash = createHash("SHA256").update(data).digest("binary");
69
- return Buffer.from(hash).subarray(0, length);
60
+ return existingJson;
70
61
  }
71
-
72
- // src/utils/crypto/encrypt.ts
73
- import { createCipheriv, createPrivateKey, createSign } from "node:crypto";
74
- function encrypt(plainText, key, iv) {
75
- const cipher = createCipheriv("aes-256-cbc", key, iv);
76
- let encrypted = cipher.update(plainText, "utf8", "base64url");
77
- encrypted += cipher.final("base64url");
78
- return encrypted;
79
- }
80
- function signature(buffer, privateKey, cert, version) {
81
- const sig = createSign("RSA-SHA256").update(buffer).sign(createPrivateKey(privateKey), "base64");
82
- return encrypt(`${sig}%${version}`, hashString(cert, 32), hashString(buffer, 16));
83
- }
84
-
85
- // src/build-plugins/constant.ts
86
- import { createLogger } from "vite";
87
62
  var id = "electron-incremental-updater";
88
63
  var bytecodeId = `${id}-bytecode`;
89
64
  var log = createLogger("info", { prefix: `[${id}]` });
90
65
  var bytecodeLog = createLogger("info", { prefix: `[${bytecodeId}]` });
91
66
 
92
- // src/build-plugins/bytecode/code.ts
67
+ // src/vite/bytecode/code.ts
93
68
  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";
94
69
  var 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';
95
-
96
- // src/build-plugins/bytecode/utils.ts
97
- import path from "node:path";
98
- import fs from "node:fs";
99
- import { spawn } from "node:child_process";
100
- import * as babel from "@babel/core";
101
- import MagicString from "magic-string";
102
- import { getPackageInfoSync } from "local-pkg";
103
70
  var electronModulePath = getPackageInfoSync("electron")?.rootPath;
104
71
  var useStrict = "'use strict';";
105
72
  var bytecodeModuleLoader = "__loader__.js";
@@ -109,13 +76,13 @@ function getElectronPath() {
109
76
  if (!electronModulePath) {
110
77
  throw new Error("Electron is not installed");
111
78
  }
112
- const pathFile = path.join(electronModulePath, "path.txt");
79
+ const pathFile = path5.join(electronModulePath, "path.txt");
113
80
  let executablePath;
114
- if (fs.existsSync(pathFile)) {
115
- executablePath = fs.readFileSync(pathFile, "utf-8");
81
+ if (fs3.existsSync(pathFile)) {
82
+ executablePath = fs3.readFileSync(pathFile, "utf-8");
116
83
  }
117
84
  if (executablePath) {
118
- electronExecPath = path.join(electronModulePath, "dist", executablePath);
85
+ electronExecPath = path5.join(electronModulePath, "dist", executablePath);
119
86
  process.env.ELECTRON_EXEC_PATH = electronExecPath;
120
87
  } else {
121
88
  throw new Error("Electron executable file is not existed");
@@ -124,23 +91,23 @@ function getElectronPath() {
124
91
  return electronExecPath;
125
92
  }
126
93
  function getBytecodeCompilerPath() {
127
- const scriptPath = path.join(electronModulePath, "bytenode.cjs");
128
- if (!fs.existsSync(scriptPath)) {
129
- fs.writeFileSync(scriptPath, bytecodeGeneratorScript);
94
+ const scriptPath = path5.join(electronModulePath, "EIU_bytenode.cjs");
95
+ if (!fs3.existsSync(scriptPath)) {
96
+ fs3.writeFileSync(scriptPath, bytecodeGeneratorScript);
130
97
  }
131
98
  return scriptPath;
132
99
  }
133
100
  function toRelativePath(filename, importer) {
134
- const relPath = path.posix.relative(path.dirname(importer), filename);
101
+ const relPath = path5.posix.relative(path5.dirname(importer), filename);
135
102
  return relPath.startsWith(".") ? relPath : `./${relPath}`;
136
103
  }
137
104
  function compileToBytecode(code) {
138
105
  let data = Buffer.from([]);
139
- const logErr = (...args) => log.error(args.join(" "), { timestamp: true });
106
+ const logErr = (...args) => bytecodeLog.error(args.join(" "), { timestamp: true });
140
107
  const electronPath = getElectronPath();
141
108
  const bytecodePath = getBytecodeCompilerPath();
142
- return new Promise((resolve2, reject) => {
143
- const proc = spawn(electronPath, [bytecodePath], {
109
+ return new Promise((resolve, reject) => {
110
+ const proc = cp.spawn(electronPath, [bytecodePath], {
144
111
  env: { ELECTRON_RUN_AS_NODE: "1" },
145
112
  stdio: ["pipe", "pipe", "pipe", "ipc"]
146
113
  });
@@ -151,7 +118,7 @@ function compileToBytecode(code) {
151
118
  if (proc.stdout) {
152
119
  proc.stdout.on("data", (chunk) => data = Buffer.concat([data, chunk]));
153
120
  proc.stdout.on("error", (err) => logErr(err));
154
- proc.stdout.on("end", () => resolve2(data));
121
+ proc.stdout.on("end", () => resolve(data));
155
122
  }
156
123
  if (proc.stderr) {
157
124
  proc.stderr.on("data", (chunk) => logErr("Error: ", chunk.toString()));
@@ -159,54 +126,111 @@ function compileToBytecode(code) {
159
126
  }
160
127
  proc.addListener("error", (err) => logErr(err));
161
128
  proc.on("error", (err) => reject(err));
162
- proc.on("exit", () => resolve2(data));
129
+ proc.on("exit", () => resolve(data));
163
130
  });
164
131
  }
165
- function convertArrowToFunction(code) {
132
+ function convertArrowFunctionAndTemplate(code) {
166
133
  const result = babel.transform(code, {
167
- plugins: ["@babel/plugin-transform-arrow-functions"]
134
+ plugins: ["@babel/plugin-transform-arrow-functions", "@babel/plugin-transform-template-literals"]
168
135
  });
169
136
  return {
170
137
  code: result?.code || code,
171
138
  map: result?.map
172
139
  };
173
140
  }
174
- function escapeRegExpString(str) {
175
- return str.replace(/\\/g, "\\\\").replace(/[|{}()[\]^$+*?.]/g, "\\$&");
141
+ var decodeFn = ";function _0xstr_(a,b){return String.fromCharCode.apply(0,a.map(function(x){return x-b}))};";
142
+ function obfuscateString(input, offset = ~~(Math.random() * 16) + 1) {
143
+ const hexArray = input.split("").map((c) => "0x" + (c.charCodeAt(0) + offset).toString(16));
144
+ return `_0xstr_([${hexArray.join(",")}],${offset})`;
176
145
  }
177
- function convertString(code, strings, sourcemap) {
178
- let s = null;
179
- for (const str of strings.filter(Boolean)) {
180
- const regex = new RegExp(`["']${escapeRegExpString(str)}["']`, "g");
181
- s ||= new MagicString(code).replace(regex, (match) => obfuscateString(match.slice(1, -1)));
146
+ function convertLiteral(code, sourcemap, offset) {
147
+ const s = new MagicString(code);
148
+ let hasTransformed = false;
149
+ const ast = babel.parse(code, { ast: true });
150
+ if (!ast) {
151
+ throw new Error("Cannot parse code");
152
+ }
153
+ babel.traverse(ast, {
154
+ StringLiteral(path6) {
155
+ const parent = path6.parent;
156
+ const node = path6.node;
157
+ if (parent.type === "CallExpression") {
158
+ if (parent.callee.type === "Identifier" && parent.callee.name === "require") {
159
+ return;
160
+ }
161
+ if (parent.callee.type === "Import") {
162
+ return;
163
+ }
164
+ }
165
+ if (parent.type.startsWith("Export")) {
166
+ return;
167
+ }
168
+ if (parent.type.startsWith("Import")) {
169
+ return;
170
+ }
171
+ if (parent.type === "ObjectMethod" && parent.key === node) {
172
+ return;
173
+ }
174
+ if (parent.type === "ObjectProperty" && parent.key === node) {
175
+ const result2 = `[${obfuscateString(node.value, offset)}]`;
176
+ const start2 = node.start;
177
+ const end2 = node.end;
178
+ if (start2 && end2) {
179
+ s.overwrite(start2, end2, result2);
180
+ hasTransformed = true;
181
+ }
182
+ return;
183
+ }
184
+ if (!node.value.trim()) {
185
+ return;
186
+ }
187
+ const result = obfuscateString(node.value, offset);
188
+ const start = node.start;
189
+ const end = node.end;
190
+ if (start && end) {
191
+ s.overwrite(start, end, result);
192
+ hasTransformed = true;
193
+ }
194
+ }
195
+ });
196
+ if (hasTransformed) {
197
+ s.append("\n").append(decodeFn);
182
198
  }
183
- return s ? {
199
+ return {
184
200
  code: s.toString(),
185
- map: sourcemap ? s.generateMap({ hires: "boundary" }) : null
186
- } : { code };
201
+ map: sourcemap ? s.generateMap({ hires: true }) : void 0
202
+ };
187
203
  }
188
- function obfuscateString(input) {
189
- const offset = Math.floor(Math.random() * 2 << 4) + 1;
190
- const hexArray = Array.from(input).map((c) => "0x" + (c.charCodeAt(0) + offset).toString(16));
191
- const decodeFn = `function(a,b){return String.fromCharCode.apply(null,a.map(x=>+x-b))}`;
192
- return `(${decodeFn})([${hexArray.join(",")}],${offset})`;
204
+
205
+ // src/vite/utils.ts
206
+ function readableSize(size) {
207
+ const units = ["B", "KB", "MB", "GB"];
208
+ let i = 0;
209
+ while (size >= 1024 && i < units.length - 1) {
210
+ size /= 1024;
211
+ i++;
212
+ }
213
+ return `${size.toFixed(2)} ${units[i]}`;
193
214
  }
194
215
 
195
- // src/build-plugins/build.ts
216
+ // src/vite/build.ts
196
217
  async function buildAsar({
197
218
  version,
198
219
  asarOutputPath,
199
220
  gzipPath,
200
221
  electronDistPath,
201
- rendererDistPath
222
+ rendererDistPath,
223
+ generateGzipFile
202
224
  }) {
203
- renameSync(rendererDistPath, join(electronDistPath, "renderer"));
204
- writeFileSync2(join(electronDistPath, "version"), version);
225
+ fs3.renameSync(rendererDistPath, path5.join(electronDistPath, "renderer"));
226
+ fs3.writeFileSync(path5.join(electronDistPath, "version"), version);
205
227
  await Asar.createPackage(electronDistPath, asarOutputPath);
206
- await zipFile(asarOutputPath, gzipPath);
228
+ const buf = await generateGzipFile(fs3.readFileSync(asarOutputPath));
229
+ fs3.writeFileSync(gzipPath, buf);
230
+ log.info(`Build update asar to '${gzipPath}' [${readableSize(buf.length)}]`, { timestamp: true });
231
+ return buf;
207
232
  }
208
233
  async function buildVersion({
209
- gzipPath,
210
234
  versionPath,
211
235
  privateKey,
212
236
  cert,
@@ -214,52 +238,35 @@ async function buildVersion({
214
238
  minimumVersion,
215
239
  generateSignature,
216
240
  generateVersionJson
217
- }) {
241
+ }, asarBuffer) {
218
242
  let _json = {
219
243
  beta: {
220
244
  minimumVersion: version,
221
245
  signature: "",
222
- size: 0,
223
246
  version
224
247
  },
225
248
  minimumVersion: version,
226
249
  signature: "",
227
- size: 0,
228
250
  version
229
251
  };
230
- if (existsSync2(versionPath)) {
252
+ if (fs3.existsSync(versionPath)) {
231
253
  try {
232
- const oldVersionJson = JSON.parse(readFileSync2(versionPath, "utf-8"));
254
+ const oldVersionJson = JSON.parse(fs3.readFileSync(versionPath, "utf-8"));
233
255
  if (isUpdateJSON(oldVersionJson)) {
234
256
  _json = oldVersionJson;
235
257
  } else {
236
- log.warn("old version json is invalid, ignore it");
258
+ log.warn("Old version json is invalid, ignore it", { timestamp: true });
237
259
  }
238
- } catch (error) {
260
+ } catch {
239
261
  }
240
262
  }
241
- const buffer = readFileSync2(gzipPath);
242
- const sig = await (generateSignature ?? signature)(buffer, privateKey, cert, version);
243
- if (generateVersionJson) {
244
- _json = await generateVersionJson(_json, buffer, sig, version, minimumVersion);
245
- if (!isUpdateJSON(_json)) {
246
- throw new Error("invalid version info");
247
- }
248
- } else {
249
- _json.beta = {
250
- version,
251
- minimumVersion,
252
- signature: sig,
253
- size: buffer.length
254
- };
255
- if (!parseVersion(version).stage) {
256
- _json.version = version;
257
- _json.minimumVersion = minimumVersion;
258
- _json.signature = sig;
259
- _json.size = buffer.length;
260
- }
263
+ const sig = await generateSignature(asarBuffer, privateKey, cert, version);
264
+ _json = await generateVersionJson(_json, sig, version, minimumVersion);
265
+ if (!isUpdateJSON(_json)) {
266
+ throw new Error("Invalid version info");
261
267
  }
262
- writeFileSync2(versionPath, JSON.stringify(_json, null, 2));
268
+ fs3.writeFileSync(versionPath, JSON.stringify(_json, null, 2));
269
+ log.info(`build version info to '${versionPath}'`, { timestamp: true });
263
270
  }
264
271
  async function buildEntry({
265
272
  sourcemap,
@@ -268,7 +275,7 @@ async function buildEntry({
268
275
  entryOutputDirPath,
269
276
  nativeModuleEntryMap,
270
277
  overrideEsbuildOptions
271
- }, define, protectedStrings) {
278
+ }, define, bytecodeOptions) {
272
279
  const option = mergeConfig(
273
280
  {
274
281
  entryPoints: {
@@ -284,6 +291,7 @@ async function buildEntry({
284
291
  entryNames: "[dir]/[name]",
285
292
  assetNames: "[dir]/[name]",
286
293
  external: ["electron", "original-fs"],
294
+ treeShaking: true,
287
295
  loader: {
288
296
  ".node": "empty"
289
297
  },
@@ -292,62 +300,73 @@ async function buildEntry({
292
300
  overrideEsbuildOptions ?? {}
293
301
  );
294
302
  const { metafile } = await build(option);
295
- if (protectedStrings === void 0) {
303
+ if (!bytecodeOptions?.enable) {
296
304
  return;
297
305
  }
298
- const filePaths = Object.keys(metafile?.outputs ?? []);
306
+ const filePaths = Object.keys(metafile?.outputs ?? []).filter((filePath) => filePath.endsWith("js"));
299
307
  for (const filePath of filePaths) {
300
- let code = readFileSync2(filePath, "utf-8");
301
- const fileName = basename(filePath);
308
+ let code = fs3.readFileSync(filePath, "utf-8");
309
+ const fileName = path5.basename(filePath);
302
310
  const isEntry = fileName.endsWith("entry.js");
303
- if (isEntry) {
304
- code = code.replace(
305
- /(`-----BEGIN CERTIFICATE-----[\s\S]*-----END CERTIFICATE-----\n`)/,
306
- (_, cert) => `"${cert.slice(1, -1).replace(/\n/g, "\\n")}"`
307
- );
311
+ let transformedCode = convertLiteral(convertArrowFunctionAndTemplate(code).code).code;
312
+ if (bytecodeOptions.beforeCompile) {
313
+ const result = await bytecodeOptions.beforeCompile(transformedCode, filePath);
314
+ if (result) {
315
+ transformedCode = result;
316
+ }
308
317
  }
309
- const transformedCode = convertString(
310
- convertArrowToFunction(code).code,
311
- [...protectedStrings, ...isEntry ? getCert(code) : []]
312
- ).code;
313
318
  const buffer = await compileToBytecode(transformedCode);
314
- writeFileSync2(`${filePath}c`, buffer);
315
- writeFileSync2(
319
+ fs3.writeFileSync(
316
320
  filePath,
317
321
  `${isEntry ? bytecodeModuleLoaderCode : useStrict}${isEntry ? "" : "module.exports = "}require("./${fileName}c")`
318
322
  );
323
+ fs3.writeFileSync(`${filePath}c`, buffer);
319
324
  bytecodeLog.info(
320
- `${filePath} => ${(buffer.byteLength / 1e3).toFixed(2)} kB`,
325
+ `${filePath} [${(buffer.byteLength / 1e3).toFixed(2)} kB]`,
321
326
  { timestamp: true }
322
327
  );
323
328
  }
324
- bytecodeLog.info(`${filePaths.length} bundles compiled into bytecode`, { timestamp: true });
329
+ bytecodeLog.info(`${filePaths.length} file${filePaths.length > 1 ? "s" : ""} compiled into bytecode`, { timestamp: true });
325
330
  }
326
- function getCert(code) {
327
- const cert = code.match(/-----BEGIN CERTIFICATE-----[\s\S]*-----END CERTIFICATE-----\\n/)?.[0];
328
- return cert ? [cert] : [];
331
+ async function defaultZipFile(buffer) {
332
+ return new Promise((resolve, reject) => {
333
+ zlib.brotliCompress(buffer, (err, buffer2) => {
334
+ if (err) {
335
+ reject(err);
336
+ } else {
337
+ resolve(buffer2);
338
+ }
339
+ });
340
+ });
341
+ }
342
+ function hashBuffer(data, length) {
343
+ const hash = crypto.createHash("SHA256").update(data).digest("binary");
344
+ return Buffer.from(hash).subarray(0, length);
345
+ }
346
+ function aesEncrypt(plainText, key, iv) {
347
+ const cipher = crypto.createCipheriv("aes-256-cbc", key, iv);
348
+ return cipher.update(plainText, "utf8", "base64url") + cipher.final("base64url");
349
+ }
350
+ function defaultSignature(buffer, privateKey, cert, version) {
351
+ const sig = crypto.createSign("RSA-SHA256").update(buffer).sign(crypto.createPrivateKey(privateKey), "base64");
352
+ return aesEncrypt(`${sig}%${version}`, hashBuffer(cert, 32), hashBuffer(buffer, 16));
329
353
  }
330
-
331
- // src/build-plugins/key.ts
332
- import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "node:fs";
333
- import { dirname } from "node:path";
334
- import { generate } from "selfsigned";
335
354
  function generateKeyPair(keyLength, subject, days, privateKeyPath, certPath) {
336
- const privateKeyDir = dirname(privateKeyPath);
337
- if (!existsSync3(privateKeyDir)) {
338
- mkdirSync(privateKeyDir, { recursive: true });
355
+ const privateKeyDir = path5.dirname(privateKeyPath);
356
+ if (!fs3.existsSync(privateKeyDir)) {
357
+ fs3.mkdirSync(privateKeyDir, { recursive: true });
339
358
  }
340
- const certDir = dirname(certPath);
341
- if (!existsSync3(certDir)) {
342
- mkdirSync(certDir, { recursive: true });
359
+ const certDir = path5.dirname(certPath);
360
+ if (!fs3.existsSync(certDir)) {
361
+ fs3.mkdirSync(certDir, { recursive: true });
343
362
  }
344
363
  const { cert, private: privateKey } = generate(subject, {
345
364
  keySize: keyLength,
346
365
  algorithm: "sha256",
347
366
  days
348
367
  });
349
- writeFileSync3(privateKeyPath, privateKey.replace(/\r\n?/g, "\n"));
350
- writeFileSync3(certPath, cert.replace(/\r\n?/g, "\n"));
368
+ fs3.writeFileSync(privateKeyPath, privateKey.replace(/\r\n?/g, "\n"));
369
+ fs3.writeFileSync(certPath, cert.replace(/\r\n?/g, "\n"));
351
370
  }
352
371
  function parseKeys({
353
372
  keyLength,
@@ -356,21 +375,29 @@ function parseKeys({
356
375
  subject,
357
376
  days
358
377
  }) {
359
- const keysDir = dirname(privateKeyPath);
360
- !existsSync3(keysDir) && mkdirSync(keysDir);
361
- if (!existsSync3(privateKeyPath) || !existsSync3(certPath)) {
362
- log.warn("no key pair found, generate new key pair");
378
+ const keysDir = path5.dirname(privateKeyPath);
379
+ let privateKey = process.env.UPDATER_PK;
380
+ let cert = process.env.UPDATER_CERT;
381
+ if (privateKey && cert) {
382
+ log.info("Use `UPDATER_PK` and `UPDATER_CERT` from environment variables", { timestamp: true });
383
+ return { privateKey, cert };
384
+ }
385
+ if (!fs3.existsSync(keysDir)) {
386
+ fs3.mkdirSync(keysDir);
387
+ }
388
+ if (!fs3.existsSync(privateKeyPath) || !fs3.existsSync(certPath)) {
389
+ log.info("No key pair found, generate new key pair", { timestamp: true });
363
390
  generateKeyPair(keyLength, parseSubjects(subject), days, privateKeyPath, certPath);
364
391
  }
365
- const privateKey = process.env.UPDATER_PK || readFileSync3(privateKeyPath, "utf-8");
366
- const cert = process.env.UPDATER_CERT || readFileSync3(certPath, "utf-8");
392
+ privateKey = fs3.readFileSync(privateKeyPath, "utf-8");
393
+ cert = fs3.readFileSync(certPath, "utf-8");
367
394
  return { privateKey, cert };
368
395
  }
369
396
  function parseSubjects(subject) {
370
397
  return Object.entries(subject).filter(([_, value]) => !!value).map(([name, value]) => ({ name, value }));
371
398
  }
372
399
 
373
- // src/build-plugins/option.ts
400
+ // src/vite/option.ts
374
401
  function parseOptions(pkg, sourcemap = false, minify = false, options = {}) {
375
402
  const {
376
403
  minimumVersion = "0.0.0",
@@ -394,24 +421,27 @@ function parseOptions(pkg, sourcemap = false, minify = false, options = {}) {
394
421
  privateKeyPath = "keys/private.pem",
395
422
  certPath = "keys/cert.pem",
396
423
  keyLength = 2048,
397
- certInfo = {},
398
- overrideGenerator = {}
424
+ certInfo: {
425
+ subject = {
426
+ commonName: pkg.name,
427
+ organizationName: `org.${pkg.name}`
428
+ },
429
+ days = 3650
430
+ } = {}
431
+ } = {},
432
+ overrideGenerator: {
433
+ generateGzipFile = defaultZipFile,
434
+ generateSignature = defaultSignature,
435
+ generateVersionJson = defaultVersionJsonGenerator
399
436
  } = {}
400
437
  } = options;
401
- const { generateSignature, generateVersionJson } = overrideGenerator;
402
- let {
403
- subject = {
404
- commonName: pkg.name,
405
- organizationName: `org.${pkg.name}`
406
- },
407
- days = 3650
408
- } = certInfo;
409
438
  const buildAsarOption = {
410
439
  version: pkg.version,
411
440
  asarOutputPath,
412
441
  gzipPath,
413
442
  electronDistPath,
414
- rendererDistPath
443
+ rendererDistPath,
444
+ generateGzipFile
415
445
  };
416
446
  const buildEntryOption = {
417
447
  minify: entryMinify ?? minify,
@@ -431,7 +461,6 @@ function parseOptions(pkg, sourcemap = false, minify = false, options = {}) {
431
461
  const buildVersionOption = {
432
462
  version: pkg.version,
433
463
  minimumVersion,
434
- gzipPath,
435
464
  privateKey,
436
465
  cert,
437
466
  versionPath,
@@ -440,22 +469,17 @@ function parseOptions(pkg, sourcemap = false, minify = false, options = {}) {
440
469
  };
441
470
  return { buildAsarOption, buildEntryOption, buildVersionOption, postBuild, cert };
442
471
  }
443
-
444
- // src/build-plugins/bytecode/index.ts
445
- import path2 from "node:path";
446
- import fs2 from "node:fs";
447
- import { createFilter, normalizePath } from "vite";
448
- import MagicString2 from "magic-string";
449
- function bytecodePlugin(isBuild, env, options = {}) {
450
- if (!isBuild) {
451
- return null;
452
- }
472
+ function bytecodePlugin(env, options) {
453
473
  const {
454
- protectedStrings = [],
455
- enablePreload = false
474
+ enable,
475
+ preload = false,
476
+ beforeCompile
456
477
  } = options;
457
- if (!enablePreload && env === "preload") {
458
- 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 });
478
+ if (!enable) {
479
+ return null;
480
+ }
481
+ if (!preload && env === "preload") {
482
+ 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 });
459
483
  return null;
460
484
  }
461
485
  const filter = createFilter(/\.(m?[jt]s|[jt]sx)$/);
@@ -470,10 +494,9 @@ function bytecodePlugin(isBuild, env, options = {}) {
470
494
  config = resolvedConfig;
471
495
  },
472
496
  transform(code, id2) {
473
- if (protectedStrings.length === 0 || !filter(id2)) {
474
- return;
497
+ if (!filter(id2)) {
498
+ return convertLiteral(code, !!config.build.sourcemap);
475
499
  }
476
- return convertString(code, protectedStrings, !!config.build.sourcemap);
477
500
  },
478
501
  generateBundle(options2) {
479
502
  if (options2.format !== "es" && bytecodeRequired) {
@@ -488,14 +511,14 @@ function bytecodePlugin(isBuild, env, options = {}) {
488
511
  renderChunk(code, chunk, options2) {
489
512
  if (options2.format === "es") {
490
513
  bytecodeLog.warn(
491
- 'bytecodePlugin does not support ES module, please remove "type": "module" in package.json or set the "build.rollupOptions.output.format" option to "cjs".',
514
+ '`bytecodePlugin` does not support ES module, please remove "type": "module" in package.json or set the "build.rollupOptions.output.format" option to "cjs".',
492
515
  { timestamp: true }
493
516
  );
494
517
  return null;
495
518
  }
496
519
  if (chunk.type === "chunk") {
497
520
  bytecodeRequired = true;
498
- return convertArrowToFunction(code);
521
+ return convertArrowFunctionAndTemplate(code);
499
522
  }
500
523
  return null;
501
524
  },
@@ -510,7 +533,7 @@ function bytecodePlugin(isBuild, env, options = {}) {
510
533
  (chunk) => chunk.type === "chunk" && chunk.fileName !== bytecodeModuleLoader
511
534
  );
512
535
  const bytecodeChunks = chunks.map((chunk) => chunk.fileName);
513
- const nonEntryChunks = chunks.filter((chunk) => !chunk.isEntry).map((chunk) => path2.basename(chunk.fileName));
536
+ const nonEntryChunks = chunks.filter((chunk) => !chunk.isEntry).map((chunk) => path5.basename(chunk.fileName));
514
537
  const pattern = nonEntryChunks.map((chunk) => `(${chunk})`).join("|");
515
538
  const bytecodeRE = pattern ? new RegExp(`require\\(\\S*(?=(${pattern})\\S*\\))`, "g") : null;
516
539
  const getBytecodeLoaderBlock = (chunkFileName) => {
@@ -521,9 +544,15 @@ function bytecodePlugin(isBuild, env, options = {}) {
521
544
  const chunk = output[name];
522
545
  if (chunk.type === "chunk") {
523
546
  let _code = chunk.code;
547
+ if (beforeCompile) {
548
+ const cbResult = await beforeCompile(_code, chunk.fileName);
549
+ if (cbResult) {
550
+ _code = cbResult;
551
+ }
552
+ }
524
553
  if (bytecodeRE && _code.match(bytecodeRE)) {
525
554
  let match;
526
- const s = new MagicString2(_code);
555
+ const s = new MagicString(_code);
527
556
  while (match = bytecodeRE.exec(_code)) {
528
557
  const [prefix, chunkName] = match;
529
558
  const len = prefix.length + chunkName.length;
@@ -533,20 +562,20 @@ function bytecodePlugin(isBuild, env, options = {}) {
533
562
  }
534
563
  _code = s.toString();
535
564
  }
536
- const chunkFilePath = path2.resolve(outDir, name);
565
+ const chunkFilePath = path5.resolve(outDir, name);
537
566
  if (bytecodeChunks.includes(name)) {
538
567
  const bytecodeBuffer = await compileToBytecode(_code);
539
- fs2.writeFileSync(path2.resolve(outDir, name + "c"), bytecodeBuffer);
568
+ fs3.writeFileSync(chunkFilePath + "c", bytecodeBuffer);
540
569
  if (chunk.isEntry) {
541
570
  const bytecodeLoaderBlock = getBytecodeLoaderBlock(chunk.fileName);
542
- const bytecodeModuleBlock = `require("./${path2.basename(name) + "c"}");`;
571
+ const bytecodeModuleBlock = `require("./${path5.basename(name) + "c"}");`;
543
572
  const code = `${useStrict}
544
573
  ${bytecodeLoaderBlock}
545
574
  module.exports=${bytecodeModuleBlock}
546
575
  `;
547
- fs2.writeFileSync(chunkFilePath, code);
576
+ fs3.writeFileSync(chunkFilePath, code);
548
577
  } else {
549
- fs2.unlinkSync(chunkFilePath);
578
+ fs3.unlinkSync(chunkFilePath);
550
579
  }
551
580
  bytecodeFiles.push({ name: name + "c", size: bytecodeBuffer.length });
552
581
  } else {
@@ -573,18 +602,17 @@ module.exports=${bytecodeModuleBlock}
573
602
  _code = hasBytecodeMoudle ? _code.replace(useStrict, `${useStrict}
574
603
  ${bytecodeLoaderBlock}`) : _code;
575
604
  }
576
- fs2.writeFileSync(chunkFilePath, _code);
605
+ fs3.writeFileSync(chunkFilePath, _code);
577
606
  }
578
607
  }
579
608
  })
580
609
  );
581
610
  },
582
611
  closeBundle() {
583
- const outDir = `${normalizePath(path2.relative(config.root, path2.resolve(config.root, config.build.outDir)))}/`;
612
+ const outDir = `${normalizePath(path5.relative(config.root, path5.resolve(config.root, config.build.outDir)))}/`;
584
613
  bytecodeFiles.forEach((file) => {
585
- const kbs = file.size / 1e3;
586
614
  bytecodeLog.info(
587
- `${outDir}${file.name} => ${kbs.toFixed(2)} kB`,
615
+ `${outDir}${file.name} [${readableSize(file.size)}]`,
588
616
  { timestamp: true }
589
617
  );
590
618
  });
@@ -593,17 +621,19 @@ ${bytecodeLoaderBlock}`) : _code;
593
621
  }
594
622
  };
595
623
  }
596
-
597
- // src/vite.ts
598
624
  function debugStartup(args) {
599
- process.env.VSCODE_DEBUG ? console.log("[startup] Electron App") : args.startup();
625
+ if (process.env.VSCODE_DEBUG) {
626
+ console.log("[startup] Electron App");
627
+ } else {
628
+ args.startup();
629
+ }
600
630
  }
601
631
  function getMainFilePath(options) {
602
632
  let mainFilePath;
603
633
  if (typeof options === "string") {
604
- mainFilePath = basename2(options);
634
+ mainFilePath = path5.basename(options);
605
635
  } else if (Array.isArray(options)) {
606
- mainFilePath = basename2(options[0]);
636
+ mainFilePath = path5.basename(options[0]);
607
637
  } else {
608
638
  const name = options?.index ?? options?.main;
609
639
  if (!name) {
@@ -614,7 +644,7 @@ function getMainFilePath(options) {
614
644
  return mainFilePath.replace(/\.[cm]?ts$/, ".js");
615
645
  }
616
646
  function parseVersionPath(versionPath) {
617
- versionPath = normalizePath2(versionPath);
647
+ versionPath = normalizePath(versionPath);
618
648
  if (!versionPath.startsWith("./")) {
619
649
  versionPath = "./" + versionPath;
620
650
  }
@@ -628,6 +658,7 @@ async function electronWithUpdater(options) {
628
658
  preload: _preload,
629
659
  sourcemap = !isBuild,
630
660
  minify = isBuild,
661
+ buildVersionJson,
631
662
  updater,
632
663
  bytecode,
633
664
  useNotBundle = true,
@@ -635,29 +666,26 @@ async function electronWithUpdater(options) {
635
666
  } = options;
636
667
  if (!pkg) {
637
668
  log.error(`package.json not found`, { timestamp: true });
638
- return null;
669
+ return void 0;
639
670
  }
640
671
  if (!pkg.version || !pkg.name || !pkg.main) {
641
672
  log.error(`package.json not valid`, { timestamp: true });
642
- return null;
673
+ return void 0;
643
674
  }
644
675
  const _options = parseOptions(pkg, sourcemap, minify, updater);
645
- const bytecodeOptions = typeof bytecode === "object" ? bytecode : bytecode === true ? { protectedStrings: [] } : void 0;
646
- if (bytecodeOptions) {
647
- minify = false;
648
- }
676
+ const bytecodeOptions = typeof bytecode === "object" ? bytecode : bytecode === true ? { enable: true } : void 0;
649
677
  try {
650
- rmSync(_options.buildAsarOption.electronDistPath, { recursive: true, force: true });
651
- rmSync(_options.buildEntryOption.entryOutputDirPath, { recursive: true, force: true });
652
- } catch (ignore) {
678
+ fs3.rmSync(_options.buildAsarOption.electronDistPath, { recursive: true, force: true });
679
+ fs3.rmSync(_options.buildEntryOption.entryOutputDirPath, { recursive: true, force: true });
680
+ } catch {
653
681
  }
654
- log.info(`remove old files`, { timestamp: true });
682
+ log.info(`Remove old files`, { timestamp: true });
655
683
  const { buildAsarOption, buildEntryOption, buildVersionOption, postBuild, cert } = _options;
656
684
  const { entryOutputDirPath, nativeModuleEntryMap, appEntryPath } = buildEntryOption;
657
685
  sourcemap ??= isBuild || !!process.env.VSCODE_DEBUG;
658
- const _appPath = normalizePath2(join2(entryOutputDirPath, "entry.js"));
659
- if (resolve(normalizePath2(pkg.main)) !== resolve(_appPath)) {
660
- throw new Error(`wrong "main" field in package.json: "${pkg.main}", it should be "${_appPath}"`);
686
+ const _appPath = normalizePath(path5.join(entryOutputDirPath, "entry.js"));
687
+ if (path5.resolve(normalizePath(pkg.main)) !== path5.resolve(_appPath)) {
688
+ throw new Error(`Wrong "main" field in package.json: "${pkg.main}", it should be "${_appPath}"`);
661
689
  }
662
690
  const define = {
663
691
  __EIU_ELECTRON_DIST_PATH__: JSON.stringify(buildAsarOption.electronDistPath),
@@ -666,28 +694,28 @@ async function electronWithUpdater(options) {
666
694
  __EIU_MAIN_DEV_DIR__: JSON.stringify(buildAsarOption.electronDistPath),
667
695
  __EIU_MAIN_FILE__: JSON.stringify(getMainFilePath(_main.files)),
668
696
  __EIU_SIGNATURE_CERT__: JSON.stringify(cert),
669
- __EUI_VERSION_PATH__: JSON.stringify(parseVersionPath(buildVersionOption.versionPath))
697
+ __EIU_VERSION_PATH__: JSON.stringify(parseVersionPath(buildVersionOption.versionPath))
670
698
  };
671
699
  const _buildEntry = async () => {
672
700
  await buildEntry(
673
701
  buildEntryOption,
674
702
  define,
675
- isBuild ? bytecodeOptions?.protectedStrings : void 0
703
+ bytecodeOptions
676
704
  );
677
- log.info(`vite build entry to '${entryOutputDirPath}'`, { timestamp: true });
705
+ log.info(`Build entry to '${entryOutputDirPath}'`, { timestamp: true });
678
706
  };
679
707
  const _postBuild = postBuild ? async () => await postBuild({
680
708
  getPathFromEntryOutputDir(...paths) {
681
- return join2(entryOutputDirPath, ...paths);
709
+ return path5.join(entryOutputDirPath, ...paths);
682
710
  },
683
711
  copyToEntryOutputDir({ from, to, skipIfExist = true }) {
684
- if (existsSync4(from)) {
685
- const target = join2(entryOutputDirPath, to ?? basename2(from));
686
- if (!skipIfExist || !existsSync4(target)) {
712
+ if (fs3.existsSync(from)) {
713
+ const target = path5.join(entryOutputDirPath, to ?? path5.basename(from));
714
+ if (!skipIfExist || !fs3.existsSync(target)) {
687
715
  try {
688
- cpSync(from, target);
716
+ fs3.cpSync(from, target);
689
717
  } catch (error) {
690
- log.warn(`copy failed: ${error}`);
718
+ log.warn(`Copy failed: ${error}`, { timestamp: true });
691
719
  }
692
720
  }
693
721
  }
@@ -696,7 +724,8 @@ async function electronWithUpdater(options) {
696
724
  };
697
725
  let isInit = false;
698
726
  const rollupOptions = {
699
- external: (src) => src.startsWith("node:") || Object.keys("dependencies" in pkg ? pkg.dependencies : {}).includes(src)
727
+ external: (src) => src.startsWith("node:") || Object.keys("dependencies" in pkg ? pkg.dependencies : {}).includes(src) || src === "original-fs",
728
+ treeshake: true
700
729
  };
701
730
  const electronPluginOptions = {
702
731
  main: {
@@ -707,13 +736,17 @@ async function electronWithUpdater(options) {
707
736
  await _buildEntry();
708
737
  await _postBuild();
709
738
  }
710
- _main.onstart ? _main.onstart(args) : args.startup();
739
+ if (_main.onstart) {
740
+ _main.onstart(args);
741
+ } else {
742
+ args.startup();
743
+ }
711
744
  },
712
- vite: mergeConfig2(
745
+ vite: mergeConfig(
713
746
  {
714
747
  plugins: [
715
748
  !isBuild && useNotBundle ? notBundle() : void 0,
716
- bytecodeOptions && bytecodePlugin(isBuild, "main", bytecodeOptions)
749
+ bytecodeOptions && bytecodePlugin("main", bytecodeOptions)
717
750
  ],
718
751
  build: {
719
752
  sourcemap,
@@ -729,10 +762,10 @@ async function electronWithUpdater(options) {
729
762
  preload: {
730
763
  input: _preload.files,
731
764
  onstart: _preload.onstart,
732
- vite: mergeConfig2(
765
+ vite: mergeConfig(
733
766
  {
734
767
  plugins: [
735
- bytecodeOptions && bytecodePlugin(isBuild, "preload", bytecodeOptions),
768
+ bytecodeOptions && bytecodePlugin("preload", bytecodeOptions),
736
769
  {
737
770
  name: `${id}-build`,
738
771
  enforce: "post",
@@ -742,10 +775,12 @@ async function electronWithUpdater(options) {
742
775
  async closeBundle() {
743
776
  await _buildEntry();
744
777
  await _postBuild();
745
- await buildAsar(buildAsarOption);
746
- log.info(`build asar to '${buildAsarOption.asarOutputPath}'`, { timestamp: true });
747
- await buildVersion(buildVersionOption);
748
- log.info(`build version info to '${buildVersionOption.versionPath}'`, { timestamp: true });
778
+ const buffer = await buildAsar(buildAsarOption);
779
+ if (!buildVersionJson && !isCI) {
780
+ log.warn("No `buildVersionJson` setup, skip build version json. Will build in CI by default", { timestamp: true });
781
+ } else {
782
+ await buildVersion(buildVersionOption, buffer);
783
+ }
749
784
  }
750
785
  }
751
786
  ],
@@ -761,20 +796,25 @@ async function electronWithUpdater(options) {
761
796
  )
762
797
  }
763
798
  };
764
- logParsedOptions && log.info(
765
- JSON.stringify(
766
- {
767
- ...electronPluginOptions,
768
- updater: { buildAsarOption, buildEntryOption, buildVersionOption }
769
- },
770
- (key, value) => (key === "privateKey" || key === "cert") && !(typeof logParsedOptions === "object" && logParsedOptions.showKeys === true) ? "***" : value,
771
- 2
772
- ),
773
- { timestamp: true }
774
- );
799
+ if (logParsedOptions) {
800
+ log.info(
801
+ JSON.stringify(
802
+ {
803
+ ...electronPluginOptions,
804
+ updater: { buildAsarOption, buildEntryOption, buildVersionOption }
805
+ },
806
+ (key, value) => (key === "privateKey" || key === "cert") && !(typeof logParsedOptions === "object" && logParsedOptions.showKeys === true) ? "***" : value,
807
+ 2
808
+ ),
809
+ { timestamp: true }
810
+ );
811
+ }
775
812
  let extraHmrPlugin;
776
813
  if (nativeModuleEntryMap) {
777
- const files = [...Object.values(nativeModuleEntryMap), appEntryPath].map((file) => resolve(normalizePath2(file)));
814
+ const files = [
815
+ ...Object.values(nativeModuleEntryMap),
816
+ appEntryPath
817
+ ].map((file) => path5.resolve(normalizePath(file)));
778
818
  extraHmrPlugin = {
779
819
  name: `${id}-dev`,
780
820
  apply() {
@@ -794,7 +834,6 @@ async function electronWithUpdater(options) {
794
834
  }
795
835
  return [ElectronSimple(electronPluginOptions), extraHmrPlugin];
796
836
  }
797
- export {
798
- debugStartup,
799
- electronWithUpdater
800
- };
837
+ var vite_default = electronWithUpdater;
838
+
839
+ export { debugStartup, vite_default as default, electronWithUpdater };