Обфускація збірки Parcel

@afterpack/parcel-optimizer обфускує кожен запакований бандл у пам'яті як Optimizer для Parcel 2 і зупиняє збірку з помилкою (fail closed), замість того щоб випустити чанк, який у продакшені віддає 404.

@afterpack/parcel-optimizer — це Optimizer для Parcel 2, зареєстрований у .parcelrc. Він обфускує кожен запакований бандл у пам'яті, під час власної фази оптимізації Parcel, тож бандл ніколи не потрапляє на диск у відкритому вигляді.

Не @afterpack/parcel

Пакет називається @afterpack/parcel-optimizer.

Встановлення

$ npm install -D @afterpack/parcel-optimizer
// .parcelrc
{
  "extends": "@parcel/config-default",
  "optimizers": {
    "*.{js,mjs,cjs}": ["...", "@afterpack/parcel-optimizer"]
  }
}

Розгортання "..." важливе: воно зберігає стандартні оптимізатори Parcel (@parcel/optimizer-swc) і ставить AfterPack останнім. AfterPack працює після мініфікації. Peer-залежність: Parcel >=2.9.0.

Збірка

Записи в .parcelrc є просто назвами пакетів без вбудованих параметрів, тож параметри беруться з afterpack.json у корені проєкту, єдиного файлу конфігурації, який читає кожна інтеграція AfterPack:

{
  "seed": "git",
  "preset": "medium"
}

Записуються лише артефакти в корені проєкту; поруч із вашими бандлами нічого не з'являється:

  • По одному protectionMap.html на бандл, у .afterpack/<bundle>.<id>.protectionMap.html.
  • Source map, коли його вмикає політика sourceMap. Він повертається в Parcel, а не записується напряму, тож Parcel сам дає йому ім'я і видає його з власним хешем вмісту.

.afterpack/ має власний .gitignore; плагін ніколи не редагує .gitignore вашого проєкту.

Перевірка

Оскільки об'єднаного звіту немає, перевіряйте потрібний бандл напряму: .afterpack/<bundle-name>.<id>.protectionMap.html для кожної точки входу або лінивого чанка, які запакував Parcel. Source map у продакшені вимкнені за замовчуванням; щоб це змінити, задайте "sourceMap": { "enabled": true } в afterpack.json. Кожен звіт містить увесь ваш вихідний код. Ніколи не комітьте його і не роздавайте з сервера.

Щоб перевіряти розгортання, додайте reporter плагіна до команди збірки. Тоді кожна збірка записує квитанцію захисту в .afterpack/receipts/ у корені вашого проєкту, по одній на кожен цільовий каталог виводу (.afterpack/receipts/dist.json для dist). afterpack verify завершується помилкою, коли файл змінився після обфускації або коли квитанції немає:

parcel build index.html --reporter @afterpack/parcel-optimizer/reporter
npx afterpack@latest verify dist

Параметри

Там можна задати кожен ключ конфігурації, наприклад { "preset": "hard", "complexity": 40 }. protectionMap.enabled залежить від того, чи має бандл source map, а не від режиму збірки, і звіт завжди потрапляє в .afterpack/, внесений у gitignore. Параметра build.backup у цього плагіна немає. Якщо його задати, буде попередження. Явні параметри завжди мають пріоритет над продакшн-значеннями за замовчуванням.

Директиви

Плагін зчитує директиви з вашого вихідного коду. Якщо придатного source map немає, проходу нічого відновлювати, і він про це повідомляє.

Директива в модулі точки входу не відновлюється. Прохід повідомляє DIAG_DIRECTIVE_SOURCE_UNRESOLVED і називає модуль. Директиви в усіх інших модулях, що не є точкою входу, працюють як звичайно, щойно source map увімкнено. Якщо вам потрібна директива в модулі точки входу, перенесіть захищений код в імпортований модуль.

Особливі випадки

Заповнювачі хешу вмісту можуть зламати збірку

Коли хешування вмісту увімкнене (за замовчуванням для parcel build) і ціль не підтримує нативний ESM, заповнювачі хешу вмісту Parcel опиняються всередині обфускованих рядків, і кожен чанк, отриманий розділенням коду, у продакшені віддає 404.

Цей плагін виявляє такий конфлікт і зупиняє збірку з помилкою, замість того щоб її випустити:

AfterPack obfuscated 1 Parcel content-hash placeholder(s) in app.[hash].js (HASH_REF_...).
  hint: Run `parcel build --no-content-hash` (chunk URLs stop being content-addressed).
  hint: Or set "complexity": 0 in afterpack.json to ship minify-only.

Стандартний ESM-результат Parcel це не зачіпає.

Варто знати

  • Один прохід на бандл, у робочих процесах. Parcel запускає оптимізатори для кожного бандла окремо, а бандли, запаковані в різних робочих процесах, не можуть мати спільного стану. Бандли, запаковані в одному процесі, мають спільний сід. Щоб закріпити один сід для всіх робочих процесів, задайте seed явно або експортуйте AFTERPACK_seed.
  • Parcel кешує результат оптимізаторів. Перезбірка з прогрітим .parcel-cache і незміненими вхідними даними не запускає AfterPack повторно і не змінює незакріплений сід. Очистьте .parcel-cache для нового проходу; збірки в CI і так починаються з холодного кешу.
  • Два нешкідливі попередження. Parcel друкує ES module dependencies are experimental (кожен пакет AfterPack є ESM) і повідомляє, що плагін contains non-statically analyzable dependencies (це визначення нативного бінарного файлу рушія під час виконання). Жодне з них не впливає на збірку.

Без плагіна: CLI

Зберіть проєкт Parcel як завжди, а потім вкажіть AfterPack на результат:

$ npx parcel build
$ npx afterpack@latest dist/ --preset=medium --seed=git

Без аргументу шляху CLI визначає dist/, типовий каталог виводу Parcel; якщо ваш відрізняється, вкажіть каталог явно. AfterPack обходить каталог, збирає кожен .js/.mjs/.cjs і обфускує їх на місці. Запуск через CLI не має захисту від конфлікту хешу вмісту і створює одну об'єднану protectionMap.html для всього каталогу замість однієї на бандл. Правило fail closed усе одно діє: відсутній каталог збірки, відсутність JS-файлів, діагностика рівня error/critical або файл, який рушій не зміг обфускувати, завершуються з кодом 1.

Далі

  • Фреймворки: повна матриця.
  • esbuild: ще один плагін, який відновлює директиви із source map після мініфікації.
  • Довідник CLI: повний перелік прапорців для резервного шляху.
  • Збірки та CI: сіди, dev проти prod, просування тих самих байтів.
  • Protection Map: як читати звіт.
  • Конфігурація: кожен параметр і через який інтерфейс його можна задати.
  • Пресети: драбина з п'яти щаблів.