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/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, readFileSync as readFileSync4, rmSync as rmSync2 } from "node:fs";
4
- import { createLogger, mergeConfig, normalizePath as normalizePath2 } from "vite";
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 { gunzip, gzip } from "node:zlib";
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
- gzip(buffer, (err, buffer2) => {
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
- await build({
163
- entryPoints: {
164
- entry: appEntryPath,
165
- ...nativeModuleEntryMap
166
- },
167
- bundle: true,
168
- platform: "node",
169
- outdir: entryOutputDirPath,
170
- minify,
171
- sourcemap,
172
- entryNames: "[dir]/[name]",
173
- assetNames: "[dir]/[name]",
174
- external: ["electron", "original-fs"],
175
- loader: {
176
- ".node": "empty"
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
- ...overrideEsbuildOptions
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/constant.ts
317
- var id = "electron-incremental-updater";
318
- var bytecodeId = `${id}-bytecode`;
319
- var loaderId = `${id}-loader`;
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 resolvePackageJson(root = process.cwd()) {
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 = resolvePackageJson(),
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: mergeConfig(
762
+ vite: mergeConfig2(
400
763
  {
401
- plugins: [!isBuild && useNotBundle ? notBundle() : void 0],
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: mergeConfig(
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
  };