Обфускація збірки Next.js

@afterpack/next обфускує клієнтський JavaScript усередині next build, на Turbopack чи webpack, з SSR чи статичним експортом.

@afterpack/next обфускує ваш клієнтський JavaScript зсередини next build. withAfterpack повертає конфігурацію з compiler.runAfterProductionCompile, власним хуком збірки Next, який Next викликає один раз для будь-якого з двох бандлерів.

Обгорнути конфігурацію — це вся інтеграція. Немає ні lifecycle-скрипта, ні окремої команди.

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

$ npm install -D @afterpack/next
// next.config.ts
import type { NextConfig } from "next";
import { withAfterpack } from "@afterpack/next";

const nextConfig: NextConfig = {
  // your existing config
};

export default withAfterpack(nextConfig);

Функціональна форма конфігурації Next працює так само. withAfterpack(async (phase, ctx) => ({ … })) повертає обгорнуту функцію.

Збірка

next build тепер видає обфускований результат, хоч би який бандлер створив .next/ (Turbopack чи webpack). next dev не зачіпається. Кожен виклик next build запускає хук, зокрема й завдання CI, яке викликає його напряму, і немає lifecycle-скрипта, який встановлення могло б пропустити. Якщо хук кидає виняток, збірка завершується з кодом виходу 1.

Хук обфускує лише JavaScript, який роздається клієнту: .next/static/chunks, а для статичного експорту ще й out/. .next/server ніколи не потрапляє в браузер і не обфускується. Хук запускається після компіляції і до попереднього рендерингу та статичного експорту, тож статичний експорт копіює в out/ уже обфусковані чанки.

Next записує чанки до запуску хука, тож ваші необфусковані чанки лежать на диску приблизно секунду під час збірки, і все, що може читати .next/ у цей проміжок, може їх прочитати. Плагіни, що працюють усередині конвеєра (Vite, Rollup, webpack, Parcel), натомість повертають обфускований бандл бандлеру, тож нічого необфускованого ніколи не записується.

Хук compiler.runAfterProductionCompile, який ви вже задали, компонується, а не замінюється. Ваш запускається першим, AfterPack — другим, тож ваш бачить ті самі байти, що й без встановленого AfterPack.

Параметри, передані в withAfterpack, замикаються в хуку. Вони мають пріоритет над змінними середовища AFTERPACK_*, які, своєю чергою, мають пріоритет над afterpack.json. Використовуйте paths.exclude, щоб залишити файл без змін.

Об'єднана protectionMap.html записується в .afterpack/ у корені вашого проєкту, внесений у gitignore.

Перевірка

Кожна захищена збірка записує квитанцію в .next/.afterpack-protection.json: версію рушія, сід, бандлер, ідентифікатор збірки та sha256 для кожного обфускованого файлу. Перевіряйте її на кроці розгортання:

$ npx afterpack@latest verify .

Команда завершується з ненульовим кодом, коли квитанції немає, коли вона належить іншій збірці або коли хеш будь-якого записаного в ній файлу більше не збігається зі значенням, до якого його обфускували.

Protection Map за замовчуванням записується лише тоді, коли бандлер видав source map і збірка не працює в CI (CI=true або CI=1). Next.js не видає source map для браузера, якщо не ввімкнено productionBrowserSourceMaps, тож мапа за замовчуванням вимкнена. Щоб побачити всі артефакти локально, явно ввімкніть її:

export default withAfterpack(nextConfig, { protectionMap: { enabled: true } });

Звіт потрапляє у .afterpack/protectionMap.html, внесений у gitignore. Він містить увесь ваш вихідний код. Ніколи не комітьте його і не роздавайте з сервера.

Ніколи не випускайте source map у продакшн

Файл .map містить ваш оригінальний вихідний код. Задавайте productionBrowserSourceMaps: true лише тоді, коли хочете, щоб Protection Map показувала оригінальний вихідний код. Хук прибирає map-файли з дерева, яке роздається, і в продакшні не додає коментар //# sourceMappingURL.

Параметри

Будь-який ключ конфігурації можна передати другим аргументом у withAfterpack, наприклад withAfterpack(nextConfig, { preset: "hard", complexity: 40 }).

Директиви

Директиви зчитуються плагіном із вашого вихідного коду, і щоб знайти їх у зібраних чанках, потрібен productionBrowserSourceMaps: true. Див. Директиви.

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

  • experimental.sri відхиляється. Next обчислює хеш цілісності кожного ассета під час його запису, ще до того, як може запуститися будь-який хук збірки, тож браузер блокує чанк, переписаний після цього. Приберіть experimental.sri або приберіть withAfterpack.
  • Вказуйте на .next/, а не на dist/. Якщо замість цього пакета ви використовуєте універсальний CLI, еквівалентна ціль — npx afterpack@latest .next/static/chunks, але він не прибирає за вас map-файли, які роздаються.
  • Зупинка з помилкою (fail closed) на необфускованих файлах, без можливості перевизначення. Якщо рушій не може безпечно обфускувати файл, збірка завершується помилкою і називає цей файл. Якщо файл ніколи не має обфускуватися, повністю виключіть його з проходу через paths.exclude.

Далі