@afterpack/electron одним викликом обфускує всі три програми, які випускає застосунок Electron (main, preload, renderer), і всі вони використовують один сід збірки. Якщо підключати вручну, кожна частина — це окрема збірка бандлера з власним випадковим сідом, тож частини одного застосунку виходять структурно не пов'язаними між собою.
electron-builder — не бандлер. Він пакує вже зібраний застосунок в інсталятор і ніколи не чіпає ваш JavaScript, тож над ним у будь-якому разі потрібен бандлер, незалежно від AfterPack.
Встановлення
$ npm install -D @afterpack/electron// electron.vite.config.ts
import { withAfterpack } from "@afterpack/electron";
import { defineConfig, externalizeDepsPlugin } from "electron-vite";
export default withAfterpack(
defineConfig({
main: { plugins: [externalizeDepsPlugin()] },
preload: { plugins: [externalizeDepsPlugin()] },
renderer: {},
}),
);withAfterpack обгортає всю конфігурацію electron-vite (об'єкт, функцію або проміс, які повертає defineConfig) і підключає @afterpack/vite до кожної частини, яку оголошує конфігурація, і ніколи до тієї, якої вона не оголошує. Peer-залежність: vite ^5 \| ^6 \| ^7 \| ^8.
Збірка
Як підключати прохід, залежить від того, чи ваш інструментарій налаштовує всі три частини одним викликом, чи окремою конфігурацією для кожної частини.
По одній частині
Інструментарії, які налаштовують кожну частину окремо (Electron Forge з @electron-forge/plugin-vite), натомість викликають afterpackElectron один раз для кожної конфігурації:
// vite.main.config.ts (and vite.preload.config.ts / vite.renderer.config.ts)
import { afterpackElectron } from "@afterpack/electron";
export default { plugins: [afterpackElectron({ leg: "main" })] };leg обов'язковий.
Один сід, три способи
- electron-vite (
withAfterpack) і Forge +plugin-vite: усі три конфігурації збираються в одному процесі Node, тож частини отримують спільний сід автоматично. vite-plugin-electron: теж один процес. ПоставтеafterpackElectron({ leg: "renderer" })останнім уplugins, після самогоvite-plugin-electron, щобgenerateBundleAfterPack запечатував уже готовий бандл.- Власні скрипти, які запускають кожну частину окремою командою: спільного процесу немає, тож закріпіть сід для всіх через змінну середовища:
// package.json
"build": "AFTERPACK_seed=git vite build -c vite.main.ts && vite build -c vite.renderer.ts"AFTERPACK_seed приймає ті самі значення, що й seed: ціле число, "git" (виводиться з HEAD) або будь-який інший рядок, який рушій хешує. Явний параметр seed усе одно має над ним пріоритет. Стратегія сіду пояснює, коли його закріплювати, а коли ні.
Перевірка
Protection Map записується окремо для кожної частини, у .afterpack/<leg>/protectionMap.html, коли вдалося знайти source map і збірка не працює в CI (CI=true або CI=1). Продакшн-режим її не вимикає, але electron-vite не видає source map, якщо ви їх не запросите, тож мапа за замовчуванням вимкнена. Досліджуйте main, preload і renderer окремо. Кожен звіт містить увесь ваш вихідний код. Ніколи не комітьте його і не роздавайте з сервера.
.map в app.asar веде до вашого оригінального вихідного коду. Тримайте генерацію source map вимкненою (так за замовчуванням у продакшні) для всього, що ви пакуєте.
Параметри
Будь-який ключ конфігурації працює і тут: для всіх частин через withAfterpack(config, options) або для кожної частини окремо через afterpackElectron(options). Ці параметри існують лише в цьому пакеті:
| Параметр | Тип | За замовчуванням |
|---|---|---|
leg | "main" | "preload" | "renderer" | обов'язковий в afterpackElectron |
projectRoot | string | process.cwd(). electron-vite запускає збірку renderer від кореня src/renderer/. Перевизначте його, якщо ваша збірка запускається з іншого місця |
seed за замовчуванням — одне нове значення, спільне для всіх частин у межах сесії збірки. protectionMap.enabled підпорядковується звичайному правилу щодо source map, і кожна частина записує власний звіт у .afterpack/<leg>/protectionMap.html.
// Heavier protection, reproducible per commit, applied to every leg:
withAfterpack(config, { preset: "hard", seed: "git" });Директиви
Кожна частина під капотом підключає @afterpack/vite, тож збір директив працює там так само.
Особливі випадки
Матриця підтримки
| Інструментарій | Підключення | Один сід автоматично? |
|---|---|---|
| electron-vite | withAfterpack(defineConfig({ … })), один виклик | Так |
Forge + plugin-vite | afterpackElectron({ leg }) у кожній із трьох конфігурацій | Так |
Forge + plugin-webpack | @afterpack/webpack у кожній конфігурації | Так |
vite-plugin-electron | afterpackElectron({ leg: "renderer" }), останнім | Так, якщо стоїть після цього плагіна |
| electron-builder | він пакує, а не збирає бандл; підключайте бандлер над ним | н/д |
| Власні скрипти (окремі процеси) | плагін для кожної частини або npx afterpack@latest <dir> | Ні; закріпіть AFTERPACK_seed |
Що він відхиляє
Дві перевірки з зупинкою з помилкою (fail closed) діють для будь-якої частини, підключеної через цей пакет.
bytecodePlugin. Власний bytecodePlugin з electron-vite замінює кожен згенерований чанк 3-рядковою заглушкою-завантажувачем і випускає справжній застосунок як байткод V8 (.jsc). Ці двоє не можуть працювати разом, бо байткод замінює чанк, згенерований Rollup. Збірка завершується помилкою на частині, де ввімкнено bytecodePlugin, або там, де файл .jsc/.cjsc згодом з'являється в результаті. Приберіть одне або інше.
build.backup: true. Резервні копії відхиляються повністю: виклик кидає виняток замість того, щоб продовжити, бо .backup.<hash>.js — це ваш повний оригінальний вихідний код, записаний поруч із результатом усередині дерева, яке ваш пакувальник копіює в app.asar. Кожен користувач, який встановить застосунок, отримав би його.
Той самий ризик стосується будь-якого артефакту, про який ваш пакувальник не знає, що його треба виключити. Стандартний glob files в electron-builder збігається з dot: true і за замовчуванням не виключає .afterpack/. Виключіть ці шляхи з того, що ви пакуєте:
// electron-builder
"files": ["**/*", "!.afterpack/**", "!**/*.backup.*", "!**/*.map"]
// electron-forge
packagerConfig: { ignore: [/^\/\.afterpack/, /\.backup\./, /\.map$/] }На source map видається попередження, а не відмова: .map із sourcesContent усередині app.asar — це повна деобфускація. Залишайте sourceMap: { enabled: true, emitUrl: true } лише для локальних налагоджувальних збірок і виключайте *.map з того, що ви пакуєте.
Канали IPC
Назви каналів мають збігатися байт у байт в обох бандлах. ipcMain.on("save-file", …) і ipcRenderer.send("save-file", …) — це той самий рядок з обох боків. Це звичайні рядкові літерали у звичайному згенерованому JavaScript, тож саме однаковий сід і однаковий рівень захисту (preset / complexity) для обох частин тримають дві половини узгодженими.
Якщо назва каналу складається під час виконання, а не записана літералом, тримайте цю конструкцію в модулі, який ви виключаєте через paths.exclude, або знизьте рівень захисту для цієї ділянки через запис regions у плагіні бандлера (Pro).
Без плагіна: CLI
Не хочете додавати залежність або збираєте частину чимось іншим, ніж Vite? Натомість запустіть npx afterpack@latest один раз для кожного каталогу виводу після збірки:
npx afterpack@latest out/main --seed=git
npx afterpack@latest out/renderer --seed=gitБез аргументу шляху CLI визначає out/, каталог, у який пише electron-vite; якщо ваш відрізняється, вкажіть каталог явно. Кожен запуск обходить каталог, збирає кожен .js/.mjs/.cjs і обфускує їх на місці; аргументом може бути й один файл. Закріпіть однаковий --seed для всіх запусків: git, що виводиться з поточного коміту, — найпростіший спосіб це гарантувати. Запуск CLI не має жодної з наведених вище відмов; він обфускує будь-який JavaScript, який знайде. Тримайте однакові --seed і --preset для всіх запусків.
Далі
- Фреймворки: повна матриця.
- Vite: семантика параметрів, які пересилає цей пакет.
- Довідник CLI: список прапорців для запасного шляху.
- Збірки та CI: сіди, dev проти prod, просування тих самих байтів.
- Захист логіки ліцензування в застосунку Electron: застосування цього до локальної перевірки ліцензії або пробного періоду.
- Найкращі практики → Стратегія сіду: коли закріплювати, а коли ні.
- Protection Map: як читати звіт.
- Конфігурація: кожен параметр і де його можна задати.