@afterpack/next आपके client JavaScript को next build के अंदर से obfuscate करता है। withAfterpack एक config लौटाता है जिसमें compiler.runAfterProductionCompile होता है, Next का अपना build hook, जिसे Next दोनों में से किसी भी bundler के लिए एक बार call करता है।
आपके config को wrap करना ही पूरा integration है। कोई lifecycle script नहीं, कोई अलग command नहीं।
Install
$ 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 config का function रूप भी उसी तरह काम करता है। withAfterpack(async (phase, ctx) => ({ … })) एक wrapped function लौटाता है।
Build
अब next build obfuscated output emit करता है, चाहे .next/ किसी भी bundler (Turbopack या webpack) ने बनाया हो। next dev अछूता रहता है। next build का हर invocation hook चलाता है, उसे सीधे call करने वाला CI job भी, और कोई lifecycle script नहीं है जिसे कोई install छोड़ सके। Hook throw करे तो build exit code 1 के साथ fail हो जाती है।
Hook केवल client को serve होने वाला JavaScript obfuscate करता है: .next/static/chunks, और static export के लिए out/ भी। .next/server कभी browser तक नहीं पहुँचता और obfuscate नहीं किया जाता। Hook हर content-hashed chunk का नाम बदलता है ताकि उसका नाम उसके obfuscated bytes के साथ चले, और उसके references को फिर से लिखता है। Hook compilation के बाद और prerendering व static export से पहले चलता है, इसलिए static export obfuscated chunks को out/ में copy करता है।
Hook चलने से पहले Next chunks लिख देता है, इसलिए build के दौरान लगभग एक सेकंड तक आपके unobfuscated chunks disk पर रहते हैं, और उस दौरान .next/ पढ़ सकने वाली कोई भी चीज़ उन्हें पढ़ सकती है। In-pipeline plugins (Vite, Rollup, webpack, Parcel) इसके बजाय obfuscated bundle bundler को वापस सौंपते हैं, इसलिए unobfuscated कुछ भी कभी लिखा ही नहीं जाता।
अगर आपने पहले से compiler.runAfterProductionCompile hook सेट किया है, तो उसे replace नहीं किया जाता बल्कि उसके साथ जोड़ा जाता है। आपका पहले चलता है, AfterPack का दूसरे नंबर पर, इसलिए आपके hook को वही bytes दिखते हैं जो AfterPack install न होने पर दिखते।
withAfterpack को pass किए गए options hook के अंदर बंद रहते हैं। उनकी प्राथमिकता AFTERPACK_* environment variables से ऊपर है, जिनकी प्राथमिकता afterpack.json से ऊपर है। किसी फ़ाइल को अछूता छोड़ने के लिए paths.exclude इस्तेमाल करें।
संयुक्त protectionMap.html आपके project root पर gitignored .afterpack/ में लिखा जाता है।
Verify
हर protected build आपके project root में, .next/ के बाहर, .afterpack/receipts/.next.json में एक receipt लिखती है: engine version, seed, bundler, build id, और हर obfuscated फ़ाइल का एक sha256। अपने deploy चरण में इसे assert करें:
$ npx afterpack@latest verify .यह non-zero के साथ exit करता है जब receipt गायब हो, जब वह किसी दूसरी build की हो, या जब कोई दर्ज फ़ाइल अब उस value पर hash न हो जिस पर उसे obfuscate किया गया था।
static export का out/ इन chunks की copy है, इसलिए npx afterpack@latest verify out उसे उसी receipt से जाँचता है और बदली गई हर फ़ाइल का नाम बताता है।
Protection Map डिफ़ॉल्ट रूप से केवल तब लिखा जाता है जब bundler ने source map emit किया हो और build CI (CI=true या CI=1) में न चल रही हो। जब तक productionBrowserSourceMaps चालू न हो, Next.js कोई browser source maps emit नहीं करता, इसलिए map डिफ़ॉल्ट रूप से बंद रहता है। Local पर पूरे artifacts देखने के लिए स्पष्ट रूप से opt in करें:
export default withAfterpack(nextConfig, { protectionMap: { enabled: true } });रिपोर्ट gitignored .afterpack/protectionMap.html में जाती है। इसमें आपका पूरा source होता है। इसे कभी commit या serve न करें।
एक .map फ़ाइल में आपका मूल source होता है। productionBrowserSourceMaps: true केवल तब सेट करें जब आप चाहते हैं कि Protection Map मूल source दिखाए। Hook served tree से maps हटा देता है और production में //# sourceMappingURL comment बंद रखता है।
Options
हर configuration key withAfterpack के दूसरे argument के रूप में pass की जा सकती है, उदाहरण के लिए withAfterpack(nextConfig, { preset: "hard", complexity: 40 })।
Directives
Directives को plugin आपके source से पढ़ता है, और built chunks में उनकी जगह खोजने के लिए productionBrowserSourceMaps: true चाहिए। देखें Directives।
Edge cases
experimental.sriअस्वीकार है। Next हर asset का integrity hash उसे लिखते समय ही compute करता है, किसी भी build hook के चलने से पहले, इसलिए बाद में दोबारा लिखे गए chunk को browser block कर देता है।experimental.sriहटाएँ, याwithAfterpackहटाएँ।- स्थिर
generateBuildIdबदली हुई फ़ाइलों को एक ही URL पर रखता है। अगर build id releases के बीच वही रहता है, तोstatic/<buildId>/_buildManifest.jsऔर_ssgManifest.jsअपना URL बनाए रखते हैं जबकि उनके bytes बदल जाते हैं, और build चेतावनी देता है।generateBuildIdको unset छोड़ें ताकि Next हर build के लिए random id चुने, या हर release में नया id लौटाएँ। .next/पर point करें,dist/पर नहीं। अगर आप इस package के बजाय generic CLI इस्तेमाल करते हैं, तोnpx afterpack@latest .next/static/chunksसमकक्ष target है, लेकिन यह served maps आपके लिए नहीं हटाता।- Unobfuscated फ़ाइलों पर fail-closed, बिना किसी override के। अगर engine किसी फ़ाइल को सुरक्षित रूप से obfuscate नहीं कर सकता, तो build fail हो जाती है और उस फ़ाइल का नाम बताती है। अगर किसी फ़ाइल को कभी obfuscate नहीं होना चाहिए, तो उसे
paths.excludeसे pass से पूरी तरह बाहर रखें।
आगे
- Frameworks: पूरी matrix।
- Quickstart: install, build और verify।
- Builds & CI: seeds, dev-बनाम-prod, वही bytes promote करना।
- Protection Map: रिपोर्ट पढ़ना।
- Directives: हर marker key, और वह क्या करती है।
- Configuration: engine की config surface।