| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148 |
- const fs = require("fs");
- const path = require("path");
- const { defineConfig } = require("vite");
- const { viteSingleFile } = require("vite-plugin-singlefile");
- const JavaScriptObfuscator = require("javascript-obfuscator");
- const platformBuilds = {
- applovin: { adapter: "applovin", output: "applovin" },
- unity: { adapter: "unity", output: "unity" },
- playturbo: { adapter: "playturbo", output: "playturbo" },
- mintegral: { adapter: "playturbo", output: "mintegral" },
- google: { adapter: "google", output: "google" },
- };
- // ── JS 混淆:base64 载荷保护 + 代码混淆 ─────────────────────────
- function obfuscateJsInHtml(html) {
- const scriptRegex = /(<script>)([\s\S]*?)(<\/script>)/;
- const match = html.match(scriptRegex);
- if (!match) return html;
- let jsCode = match[2];
- // 1. 找出所有 base64 数据载荷,替换为短占位符,确保 JS 语法完整
- const b64Payloads = [];
- const b64Pattern = /(data:(?:image|audio)\/[^;]*?;base64,)([A-Za-z0-9+/=]+)/g;
- let b64Match;
- while ((b64Match = b64Pattern.exec(jsCode)) !== null) {
- const idx = b64Payloads.length;
- b64Payloads.push(b64Match[2]);
- jsCode =
- jsCode.slice(0, b64Match.index + b64Match[1].length) +
- `__B64_${idx}__` +
- jsCode.slice(b64Match.index + b64Match[1].length + b64Match[2].length);
- // 重置 regex lastIndex(因为替换改变了字符串长度)
- b64Pattern.lastIndex = b64Match.index + b64Match[1].length + `__B64_${idx}__`.length;
- }
- console.log(`[obfuscate] Found ${b64Payloads.length} base64 payloads, total ${b64Payloads.reduce((s, p) => s + p.length, 0).toLocaleString()} bytes`);
- // 2. 混淆 JS(stringArrayThreshold: 0 确保占位符不被编码到 stringArray)
- const result = JavaScriptObfuscator.obfuscate(jsCode, {
- compact: true,
- controlFlowFlattening: false, // 不影响 WebGL 帧率
- deadCodeInjection: false, // 不增加体积
- debugProtection: false, // 不用 setInterval 浪费 CPU
- disableConsoleOutput: true,
- identifierNamesGenerator: "hexadecimal",
- log: false,
- numbersToExpressions: false,
- renameGlobals: false,
- selfDefending: false, // 暂时关闭排查语法错误
- simplify: true,
- splitStrings: false,
- stringArray: false, // 关闭 stringArray,保护占位符
- transformObjectKeys: false,
- unicodeEscapeSequence: false,
- });
- let obfuscatedJs = result.getObfuscatedCode();
- // 3. 恢复 base64 载荷
- for (let i = 0; i < b64Payloads.length; i++) {
- obfuscatedJs = obfuscatedJs.replace(`__B64_${i}__`, b64Payloads[i]);
- }
- return html.replace(scriptRegex, match[1] + obfuscatedJs + match[3]);
- }
- // ── 构建后处理 ───────────────────────────────────────────────────
- function patchSingleFileHtml(htmlPath) {
- if (!fs.existsSync(htmlPath)) return;
- let html = fs.readFileSync(htmlPath, "utf8");
- // 1. 移除 HTML 注释
- html = html.replace(/<!--[\s\S]*?-->/g, "");
- // 2. 清理标签属性(移除 type="module" / crossorigin)
- html = html
- .replace(/<script\s+type="module"\s+crossorigin>/g, "<script>")
- .replace(/<script\s+crossorigin\s+type="module">/g, "<script>")
- .replace(/<script\s+type="module">/g, "<script>")
- .replace(/<script\s+crossorigin>/g, "<script>")
- .replace(/<style\s+rel="stylesheet"\s+crossorigin>/g, "<style>")
- .replace(/<style\s+crossorigin\s+rel="stylesheet">/g, "<style>")
- .replace(/<style\s+crossorigin>/g, "<style>");
- // 3. JS 混淆
- html = obfuscateJsInHtml(html);
- fs.writeFileSync(htmlPath, html);
- }
- function finalizeHtmlPlugin(outDir) {
- return {
- name: "finalize-html-output",
- closeBundle() {
- const htmlPath = path.resolve(__dirname, outDir, "index.html");
- patchSingleFileHtml(htmlPath);
- },
- };
- }
- module.exports = defineConfig(({ mode }) => {
- const platformBuild = platformBuilds[mode];
- const adapter = platformBuild?.adapter || "google";
- const output = platformBuild?.output;
- const outDir = output ? `dist/${output}` : "dist";
- return {
- plugins: [viteSingleFile(), finalizeHtmlPlugin(outDir)],
- server: {
- allowedHosts: ["color2.jccytech.cn", "localhost", ".jccytech.cn"],
- },
- resolve: {
- alias: {
- "#ad-config": path.resolve(
- __dirname,
- process.env.AD_CONFIG_PATH || "src/filler/ad-config.ts",
- ),
- "./ad-platform/current": path.resolve(
- __dirname,
- `src/filler/ad-platform/adapters/${adapter}.ts`,
- ),
- },
- },
- esbuild: {
- drop: ["console", "debugger"],
- legalComments: "none",
- },
- build: {
- sourcemap: false,
- assetsInlineLimit: 100 * 1024 * 1024,
- target: "es2017",
- outDir,
- emptyOutDir: true,
- rollupOptions: {
- input: {
- main: "./index.html",
- },
- output: {
- entryFileNames: "assets/[name].js",
- chunkFileNames: "assets/[name].js",
- assetFileNames: "assets/[name][extname]",
- },
- },
- },
- };
- });
|