electron-incremental-update 1.1.0 → 1.3.0

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