Spaces:
Running
Running
| /** | |
| * Run the JavaScript obfuscator over every .js file in dist/ in-place. | |
| * | |
| * Designed to run AFTER `npm run build` (tsc → dist/) so the published | |
| * artifact is a hardened JS bundle that's painful to read or edit by hand. | |
| * Invoked automatically by `npm run build:secure`. | |
| * | |
| * node scripts/obfuscate-dist.js [optional/dist/path] | |
| */ | |
| const fs = require("fs"); | |
| const path = require("path"); | |
| let obfuscator; | |
| try { | |
| obfuscator = require("javascript-obfuscator"); | |
| } catch { | |
| console.error( | |
| "[obfuscate] javascript-obfuscator is not installed.\n" + | |
| " run: npm install --save-dev javascript-obfuscator", | |
| ); | |
| process.exit(1); | |
| } | |
| const DIST = path.resolve(process.cwd(), process.argv[2] || "dist"); | |
| if (!fs.existsSync(DIST)) { | |
| console.error(`[obfuscate] dist directory not found: ${DIST}`); | |
| console.error(" did you forget to `npm run build` first?"); | |
| process.exit(1); | |
| } | |
| const OPTIONS = { | |
| compact: true, | |
| controlFlowFlattening: true, | |
| controlFlowFlatteningThreshold: 0.6, | |
| deadCodeInjection: true, | |
| deadCodeInjectionThreshold: 0.3, | |
| stringArray: true, | |
| stringArrayEncoding: ["base64"], | |
| stringArrayThreshold: 0.75, | |
| identifierNamesGenerator: "hexadecimal", | |
| renameGlobals: false, | |
| selfDefending: true, | |
| simplify: true, | |
| transformObjectKeys: true, | |
| unicodeEscapeSequence: false, | |
| target: "node", | |
| }; | |
| const stats = { files: 0, bytesIn: 0, bytesOut: 0, skipped: 0 }; | |
| function walk(dir) { | |
| for (const name of fs.readdirSync(dir)) { | |
| const full = path.join(dir, name); | |
| const st = fs.statSync(full); | |
| if (st.isDirectory()) { | |
| walk(full); | |
| continue; | |
| } | |
| if (!full.endsWith(".js")) continue; | |
| // Skip third-party shims that were copied verbatim into dist | |
| // (none today, but future-proofing). | |
| if (full.includes(`${path.sep}vendor${path.sep}`)) { | |
| stats.skipped++; | |
| continue; | |
| } | |
| const src = fs.readFileSync(full, "utf8"); | |
| try { | |
| const out = obfuscator.obfuscate(src, OPTIONS).getObfuscatedCode(); | |
| fs.writeFileSync(full, out); | |
| stats.files++; | |
| stats.bytesIn += Buffer.byteLength(src); | |
| stats.bytesOut += Buffer.byteLength(out); | |
| } catch (err) { | |
| console.error(`[obfuscate] FAILED ${path.relative(DIST, full)}: ${err.message}`); | |
| process.exit(1); | |
| } | |
| } | |
| } | |
| console.log(`[obfuscate] hardening ${DIST}…`); | |
| walk(DIST); | |
| const ratio = stats.bytesIn ? (stats.bytesOut / stats.bytesIn).toFixed(2) : "0"; | |
| console.log( | |
| `[obfuscate] done — ${stats.files} files, ${stats.skipped} skipped, ` + | |
| `${(stats.bytesIn / 1024).toFixed(1)}KB → ${(stats.bytesOut / 1024).toFixed(1)}KB ` + | |
| `(×${ratio})`, | |
| ); | |