@playcraft/build 0.0.13 → 0.0.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/analyzers/scene-asset-collector.js +99 -9
- package/dist/base-builder.d.ts +15 -78
- package/dist/base-builder.js +34 -741
- package/dist/engines/engine-detector.d.ts +38 -0
- package/dist/engines/engine-detector.js +201 -0
- package/dist/engines/generic-adapter.d.ts +71 -0
- package/dist/engines/generic-adapter.js +378 -0
- package/dist/engines/index.d.ts +7 -0
- package/dist/engines/index.js +7 -0
- package/dist/engines/playcanvas-adapter.d.ts +85 -0
- package/dist/engines/playcanvas-adapter.js +813 -0
- package/dist/generators/config-generator.js +59 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/loaders/playcraft-loader.js +240 -5
- package/dist/platforms/adikteev.d.ts +1 -1
- package/dist/platforms/adikteev.js +30 -36
- package/dist/platforms/applovin.d.ts +1 -1
- package/dist/platforms/applovin.js +31 -36
- package/dist/platforms/base.d.ts +27 -5
- package/dist/platforms/base.js +79 -181
- package/dist/platforms/bigo.d.ts +1 -1
- package/dist/platforms/bigo.js +28 -28
- package/dist/platforms/facebook.d.ts +1 -1
- package/dist/platforms/facebook.js +21 -10
- package/dist/platforms/google.d.ts +1 -1
- package/dist/platforms/google.js +28 -21
- package/dist/platforms/index.d.ts +1 -0
- package/dist/platforms/index.js +4 -0
- package/dist/platforms/inmobi.d.ts +1 -1
- package/dist/platforms/inmobi.js +27 -34
- package/dist/platforms/ironsource.d.ts +1 -1
- package/dist/platforms/ironsource.js +37 -40
- package/dist/platforms/liftoff.d.ts +1 -1
- package/dist/platforms/liftoff.js +22 -30
- package/dist/platforms/mintegral.d.ts +10 -0
- package/dist/platforms/mintegral.js +65 -0
- package/dist/platforms/moloco.d.ts +1 -1
- package/dist/platforms/moloco.js +18 -20
- package/dist/platforms/playcraft.d.ts +1 -1
- package/dist/platforms/playcraft.js +2 -2
- package/dist/platforms/remerge.d.ts +1 -1
- package/dist/platforms/remerge.js +19 -20
- package/dist/platforms/snapchat.d.ts +1 -1
- package/dist/platforms/snapchat.js +32 -26
- package/dist/platforms/tiktok.d.ts +1 -1
- package/dist/platforms/tiktok.js +28 -24
- package/dist/platforms/unity.d.ts +1 -1
- package/dist/platforms/unity.js +30 -36
- package/dist/playable-builder.d.ts +1 -0
- package/dist/playable-builder.js +16 -2
- package/dist/types.d.ts +113 -1
- package/dist/types.js +77 -1
- package/dist/utils/ammo-detector.d.ts +9 -0
- package/dist/utils/ammo-detector.js +76 -0
- package/dist/utils/build-mode-detector.js +2 -0
- package/dist/utils/minify.d.ts +32 -0
- package/dist/utils/minify.js +82 -0
- package/dist/utils/obfuscate.d.ts +42 -0
- package/dist/utils/obfuscate.js +216 -0
- package/dist/vite/config-builder-generic.d.ts +70 -0
- package/dist/vite/config-builder-generic.js +251 -0
- package/dist/vite/config-builder.d.ts +8 -0
- package/dist/vite/config-builder.js +53 -16
- package/dist/vite/platform-configs.js +29 -1
- package/dist/vite/plugin-compress-js.d.ts +21 -0
- package/dist/vite/plugin-compress-js.js +213 -0
- package/dist/vite/plugin-esm-html-generator.js +5 -1
- package/dist/vite/plugin-obfuscate.d.ts +22 -0
- package/dist/vite/plugin-obfuscate.js +52 -0
- package/dist/vite/plugin-platform.d.ts +5 -0
- package/dist/vite/plugin-platform.js +499 -35
- package/dist/vite/plugin-playcanvas.js +21 -68
- package/dist/vite/plugin-source-builder.js +102 -21
- package/dist/vite-builder.d.ts +25 -7
- package/dist/vite-builder.js +141 -52
- package/package.json +4 -2
- package/physics/cannon-rigidbody-adapter.js +243 -22
- package/templates/__loading__.js +0 -12
- package/templates/index.esm.mjs +0 -11
- package/templates/patches/playcraft-cta-adapter.js +129 -31
- package/templates/patches/scene-physics-defaults.js +49 -0
|
@@ -22,7 +22,7 @@ export const PLATFORM_CONFIGS = {
|
|
|
22
22
|
},
|
|
23
23
|
esmSupport: {
|
|
24
24
|
enabled: true,
|
|
25
|
-
preferIIFE:
|
|
25
|
+
preferIIFE: true, // 使用 IIFE 确保 pc 全局可用,避免 ESM 模块作用域问题
|
|
26
26
|
},
|
|
27
27
|
playable: {
|
|
28
28
|
patchXhrOut: false,
|
|
@@ -415,4 +415,32 @@ export const PLATFORM_CONFIGS = {
|
|
|
415
415
|
configJsonInline: true,
|
|
416
416
|
},
|
|
417
417
|
},
|
|
418
|
+
mintegral: {
|
|
419
|
+
sizeLimit: 5 * 1024 * 1024, // 5MB (ZIP)
|
|
420
|
+
outputFormat: 'zip',
|
|
421
|
+
minifyCSS: true,
|
|
422
|
+
minifyJS: true,
|
|
423
|
+
compressImages: false, // 禁用图片压缩,原图输出效果更好
|
|
424
|
+
compressModels: true,
|
|
425
|
+
injectScripts: ['mintegralSdk'],
|
|
426
|
+
outputFileName: 'index.html',
|
|
427
|
+
includeSourcemap: false,
|
|
428
|
+
imageQuality: {
|
|
429
|
+
jpg: 80,
|
|
430
|
+
png: [0.75, 0.85],
|
|
431
|
+
webp: 80,
|
|
432
|
+
},
|
|
433
|
+
modelCompression: {
|
|
434
|
+
method: 'draco',
|
|
435
|
+
quality: 0.85,
|
|
436
|
+
},
|
|
437
|
+
esmSupport: {
|
|
438
|
+
enabled: true,
|
|
439
|
+
preferIIFE: true, // Mintegral 推荐 IIFE 确保兼容性
|
|
440
|
+
},
|
|
441
|
+
playable: {
|
|
442
|
+
inlineGameScripts: true,
|
|
443
|
+
externFiles: true, // Mintegral ZIP 支持外部文件(但非 JS/HTML 资源需 base64)
|
|
444
|
+
},
|
|
445
|
+
},
|
|
418
446
|
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Plugin } from 'vite';
|
|
2
|
+
export interface CompressJSPluginOptions {
|
|
3
|
+
/** 是否启用 JS 代码混淆保护 */
|
|
4
|
+
enabled: boolean;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* JS 代码混淆保护插件
|
|
8
|
+
*
|
|
9
|
+
* 使用 javascript-obfuscator 对 HTML 中所有内联 <script> 标签的 JS 代码
|
|
10
|
+
* 进行深度混淆,包括:
|
|
11
|
+
* - 变量名混淆
|
|
12
|
+
* - 字符串混淆(字面量转为十六进制/unicode 编码)
|
|
13
|
+
* - 控制流平坦化
|
|
14
|
+
* - 死代码注入
|
|
15
|
+
*
|
|
16
|
+
* 混淆后的代码在浏览器中无法直接阅读理解。
|
|
17
|
+
*
|
|
18
|
+
* 注意:这个插件必须在 vite-plugin-singlefile 之后执行,
|
|
19
|
+
* 确保所有资源已内联到 HTML 中。
|
|
20
|
+
*/
|
|
21
|
+
export declare function viteCompressJSPlugin(options: CompressJSPluginOptions): Plugin | null;
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import JavaScriptObfuscator from 'javascript-obfuscator';
|
|
2
|
+
import { minify } from 'terser';
|
|
3
|
+
/**
|
|
4
|
+
* JS 代码混淆保护插件
|
|
5
|
+
*
|
|
6
|
+
* 使用 javascript-obfuscator 对 HTML 中所有内联 <script> 标签的 JS 代码
|
|
7
|
+
* 进行深度混淆,包括:
|
|
8
|
+
* - 变量名混淆
|
|
9
|
+
* - 字符串混淆(字面量转为十六进制/unicode 编码)
|
|
10
|
+
* - 控制流平坦化
|
|
11
|
+
* - 死代码注入
|
|
12
|
+
*
|
|
13
|
+
* 混淆后的代码在浏览器中无法直接阅读理解。
|
|
14
|
+
*
|
|
15
|
+
* 注意:这个插件必须在 vite-plugin-singlefile 之后执行,
|
|
16
|
+
* 确保所有资源已内联到 HTML 中。
|
|
17
|
+
*/
|
|
18
|
+
export function viteCompressJSPlugin(options) {
|
|
19
|
+
if (!options.enabled) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
return {
|
|
23
|
+
name: 'vite-plugin-compress-js',
|
|
24
|
+
enforce: 'post',
|
|
25
|
+
async generateBundle(_outputOptions, bundle) {
|
|
26
|
+
for (const [fileName, asset] of Object.entries(bundle)) {
|
|
27
|
+
if (!fileName.endsWith('.html'))
|
|
28
|
+
continue;
|
|
29
|
+
if (asset.type !== 'asset')
|
|
30
|
+
continue;
|
|
31
|
+
const html = typeof asset.source === 'string'
|
|
32
|
+
? asset.source
|
|
33
|
+
: new TextDecoder().decode(asset.source);
|
|
34
|
+
console.log('[CompressJS] 开始混淆 HTML 中的 JS 代码...');
|
|
35
|
+
const result = await obfuscateAllInlineScripts(html);
|
|
36
|
+
if (result.obfuscatedCount > 0) {
|
|
37
|
+
asset.source = result.html;
|
|
38
|
+
console.log(`[CompressJS] 混淆完成: ${result.obfuscatedCount} 个脚本块`);
|
|
39
|
+
console.log(`[CompressJS] 原始大小: ${formatBytes(result.originalSize)}`);
|
|
40
|
+
console.log(`[CompressJS] 混淆后大小: ${formatBytes(result.obfuscatedSize)}`);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
console.log('[CompressJS] 未找到需要混淆的脚本块');
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* 检测一个 <script> 标签内容是否是 lz4.js 运行时
|
|
51
|
+
*/
|
|
52
|
+
function isLz4Runtime(content) {
|
|
53
|
+
return content.includes('window.lz4') && content.includes('window.Buffer') && content.includes('lz4js');
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* 检测一个 <script> 标签内容是否是 LZ4 自解压 bootstrap(由 compressEngine 生成)
|
|
57
|
+
*/
|
|
58
|
+
function isLz4Bootstrap(content) {
|
|
59
|
+
return content.includes('lz4.decompress') && content.includes('new Buffer(') && content.includes('textContent');
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* 混淆 HTML 中所有内联 <script> 标签的 JS 代码
|
|
63
|
+
*/
|
|
64
|
+
async function obfuscateAllInlineScripts(html) {
|
|
65
|
+
// 阶段 1:收集所有需要混淆的脚本块及其位置
|
|
66
|
+
const scriptRegex = /<script(?:\s[^>]*)?>([\s\S]*?)<\/script>/gi;
|
|
67
|
+
const entries = [];
|
|
68
|
+
let match;
|
|
69
|
+
while ((match = scriptRegex.exec(html)) !== null) {
|
|
70
|
+
const fullMatch = match[0];
|
|
71
|
+
const content = match[1];
|
|
72
|
+
const tagAttributes = fullMatch.substring(0, fullMatch.indexOf('>'));
|
|
73
|
+
let skip = false;
|
|
74
|
+
// 跳过有 src 属性的外部脚本
|
|
75
|
+
if (/\bsrc\s*=/i.test(tagAttributes))
|
|
76
|
+
skip = true;
|
|
77
|
+
// 跳过 type 不是 javascript 的
|
|
78
|
+
if (!skip) {
|
|
79
|
+
const typeMatch = tagAttributes.match(/\btype\s*=\s*["']([^"']+)["']/i);
|
|
80
|
+
if (typeMatch) {
|
|
81
|
+
const type = typeMatch[1].toLowerCase();
|
|
82
|
+
if (type !== 'text/javascript' && type !== 'application/javascript' && type !== '') {
|
|
83
|
+
skip = true;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// 跳过空脚本
|
|
88
|
+
if (!skip && !content.trim())
|
|
89
|
+
skip = true;
|
|
90
|
+
// 跳过 lz4.js 运行时和 bootstrap
|
|
91
|
+
if (!skip && (isLz4Runtime(content) || isLz4Bootstrap(content)))
|
|
92
|
+
skip = true;
|
|
93
|
+
// 跳过太短的脚本
|
|
94
|
+
if (!skip && content.trim().length < 100)
|
|
95
|
+
skip = true;
|
|
96
|
+
entries.push({
|
|
97
|
+
fullMatch,
|
|
98
|
+
content,
|
|
99
|
+
index: match.index,
|
|
100
|
+
skip,
|
|
101
|
+
replacement: fullMatch,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
// 阶段 2:混淆 + 二次压缩(async)
|
|
105
|
+
let obfuscatedCount = 0;
|
|
106
|
+
let originalSize = 0;
|
|
107
|
+
let obfuscatedSize = 0;
|
|
108
|
+
for (const entry of entries) {
|
|
109
|
+
if (entry.skip)
|
|
110
|
+
continue;
|
|
111
|
+
try {
|
|
112
|
+
const originalLen = Buffer.from(entry.content).length;
|
|
113
|
+
originalSize += originalLen;
|
|
114
|
+
console.log(`[CompressJS] 混淆脚本 (${formatBytes(originalLen)})...`);
|
|
115
|
+
// javascript-obfuscator:标识符混淆(关闭 stringArray 避免体积膨胀和运行时冲突)
|
|
116
|
+
const obfuscatedRaw = JavaScriptObfuscator.obfuscate(entry.content, {
|
|
117
|
+
compact: true,
|
|
118
|
+
controlFlowFlattening: false,
|
|
119
|
+
deadCodeInjection: false,
|
|
120
|
+
identifierNamesGenerator: 'hexadecimal',
|
|
121
|
+
stringArray: false,
|
|
122
|
+
splitStrings: false,
|
|
123
|
+
numbersToExpressions: false,
|
|
124
|
+
transformObjectKeys: false,
|
|
125
|
+
unicodeEscapeSequence: false,
|
|
126
|
+
disableConsoleOutput: false,
|
|
127
|
+
selfDefending: false,
|
|
128
|
+
target: 'browser',
|
|
129
|
+
// ========== 保留名单 ==========
|
|
130
|
+
reservedNames: [
|
|
131
|
+
'^pc$', '^app$', '^entity$', '^pcBootstrap$',
|
|
132
|
+
'^ASSET_PREFIX$', '^SCRIPT_PREFIX$', '^SCENE_PATH$',
|
|
133
|
+
'^CONTEXT_OPTIONS$', '^SCRIPTS$', '^INPUT_SETTINGS$',
|
|
134
|
+
'^PRELOAD_MODULES$', '^CONFIG_FILENAME$',
|
|
135
|
+
'^PlayCraftCTA$', '^jump2AppStore$', '^loadModules$',
|
|
136
|
+
'^__esmScriptClasses$', '^__esmScriptSchemas$',
|
|
137
|
+
'^__deferredESMScripts$', '^__pendingScriptRegistrations$',
|
|
138
|
+
'^Application$', '^Entity$', '^ScriptHandler$',
|
|
139
|
+
'^ScriptType$', '^Http$', '^Asset$', '^AssetRegistry$',
|
|
140
|
+
'^GraphNode$', '^Vec2$', '^Vec3$', '^Vec4$', '^Quat$',
|
|
141
|
+
'^Mat4$', '^Color$', '^Curve$', '^CurveSet$',
|
|
142
|
+
'^BoundingBox$', '^BoundingSphere$', '^Ray$',
|
|
143
|
+
'^Texture$', '^Material$', '^StandardMaterial$',
|
|
144
|
+
'^Mesh$', '^MeshInstance$', '^Model$',
|
|
145
|
+
'^AnimComponent$', '^RigidBodyComponent$',
|
|
146
|
+
'^CollisionComponent$', '^ElementComponent$',
|
|
147
|
+
'^SpriteComponent$', '^ParticleSystemComponent$',
|
|
148
|
+
'^ScreenComponent$', '^LayoutGroupComponent$',
|
|
149
|
+
'^ScrollViewComponent$', '^ButtonComponent$',
|
|
150
|
+
'^CameraComponent$', '^LightComponent$',
|
|
151
|
+
'^RenderComponent$', '^SoundComponent$',
|
|
152
|
+
'^ScriptComponent$',
|
|
153
|
+
'^GameplaySystem$', '^GameRule$',
|
|
154
|
+
'^FbPlayableAd$', '^mraid$', '^dapi$', '^ExitApi$',
|
|
155
|
+
'^BGY_MRAID$', '^snapchatCta$', '^openAppStore$',
|
|
156
|
+
'^install$', '^TJ_API$', '^NUC$', '^smxTracking$',
|
|
157
|
+
],
|
|
158
|
+
}).getObfuscatedCode();
|
|
159
|
+
// Terser 二次压缩:只做 compress,不做 mangle
|
|
160
|
+
let obfuscated = obfuscatedRaw;
|
|
161
|
+
try {
|
|
162
|
+
const terserResult = await minify(obfuscatedRaw, {
|
|
163
|
+
compress: {
|
|
164
|
+
passes: 2,
|
|
165
|
+
drop_console: true,
|
|
166
|
+
drop_debugger: true,
|
|
167
|
+
},
|
|
168
|
+
mangle: false,
|
|
169
|
+
format: {
|
|
170
|
+
comments: false,
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
if (terserResult.code) {
|
|
174
|
+
obfuscated = terserResult.code;
|
|
175
|
+
console.log(`[CompressJS] 二次压缩: ${formatBytes(Buffer.from(obfuscatedRaw).length)} → ${formatBytes(Buffer.from(obfuscated).length)}`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
catch (e) {
|
|
179
|
+
console.warn(`[CompressJS] 二次压缩失败,使用混淆后原始代码: ${e.message}`);
|
|
180
|
+
}
|
|
181
|
+
const obfuscatedLen = Buffer.from(obfuscated).length;
|
|
182
|
+
obfuscatedSize += obfuscatedLen;
|
|
183
|
+
obfuscatedCount++;
|
|
184
|
+
const tagOpen = entry.fullMatch.substring(0, entry.fullMatch.indexOf('>') + 1);
|
|
185
|
+
entry.replacement = `${tagOpen}${obfuscated}</script>`;
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
console.warn(`[CompressJS] 混淆脚本失败: ${error.message}`);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
// 阶段 3:从后往前替换,避免偏移量变化
|
|
192
|
+
let newHtml = html;
|
|
193
|
+
for (let i = entries.length - 1; i >= 0; i--) {
|
|
194
|
+
const entry = entries[i];
|
|
195
|
+
if (entry.skip)
|
|
196
|
+
continue;
|
|
197
|
+
newHtml = newHtml.substring(0, entry.index) + entry.replacement + newHtml.substring(entry.index + entry.fullMatch.length);
|
|
198
|
+
}
|
|
199
|
+
return {
|
|
200
|
+
html: newHtml,
|
|
201
|
+
obfuscatedCount,
|
|
202
|
+
originalSize,
|
|
203
|
+
obfuscatedSize,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
function formatBytes(bytes) {
|
|
207
|
+
if (bytes === 0)
|
|
208
|
+
return '0 B';
|
|
209
|
+
const k = 1024;
|
|
210
|
+
const sizes = ['B', 'KB', 'MB', 'GB'];
|
|
211
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
212
|
+
return `${(bytes / Math.pow(k, i)).toFixed(2)} ${sizes[i]}`;
|
|
213
|
+
}
|
|
@@ -730,9 +730,13 @@ ${deferWrapper}`);
|
|
|
730
730
|
compress: {
|
|
731
731
|
drop_console: true,
|
|
732
732
|
drop_debugger: true,
|
|
733
|
+
passes: 3,
|
|
734
|
+
},
|
|
735
|
+
mangle: {
|
|
736
|
+
toplevel: true,
|
|
733
737
|
},
|
|
734
738
|
format: {
|
|
735
|
-
comments: false,
|
|
739
|
+
comments: false,
|
|
736
740
|
},
|
|
737
741
|
} : undefined,
|
|
738
742
|
rollupOptions: {
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vite 插件:代码混淆 + fflate 压缩
|
|
3
|
+
*
|
|
4
|
+
* 在所有其他插件处理完成后,对最终 HTML 中的 IIFE 脚本进行:
|
|
5
|
+
* 1. javascript-obfuscator 混淆
|
|
6
|
+
* 2. fflate deflate 压缩
|
|
7
|
+
* 3. base64 编码 + 自解压 bootstrap 包装
|
|
8
|
+
*/
|
|
9
|
+
import type { Plugin } from 'vite';
|
|
10
|
+
import { type ObfuscateLevel } from '../utils/obfuscate.js';
|
|
11
|
+
export interface ObfuscatePluginOptions {
|
|
12
|
+
enabled: boolean;
|
|
13
|
+
level: ObfuscateLevel;
|
|
14
|
+
/** 是否已经注入了 fflate 运行时(如 compressEngine 已启用) */
|
|
15
|
+
fflateRuntimeInjected: boolean;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* 代码混淆插件
|
|
19
|
+
*
|
|
20
|
+
* 在 generateBundle 阶段对 HTML 中的 IIFE 脚本块进行混淆+压缩
|
|
21
|
+
*/
|
|
22
|
+
export declare function viteObfuscatePlugin(options: ObfuscatePluginOptions): Plugin;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { obfuscateAndCompress } from '../utils/obfuscate.js';
|
|
2
|
+
/**
|
|
3
|
+
* 代码混淆插件
|
|
4
|
+
*
|
|
5
|
+
* 在 generateBundle 阶段对 HTML 中的 IIFE 脚本块进行混淆+压缩
|
|
6
|
+
*/
|
|
7
|
+
export function viteObfuscatePlugin(options) {
|
|
8
|
+
return {
|
|
9
|
+
name: 'vite-plugin-obfuscate',
|
|
10
|
+
enforce: 'post',
|
|
11
|
+
async generateBundle(_outputOptions, bundle) {
|
|
12
|
+
if (!options.enabled)
|
|
13
|
+
return;
|
|
14
|
+
// 找到 HTML 文件
|
|
15
|
+
for (const [fileName, chunk] of Object.entries(bundle)) {
|
|
16
|
+
if (!fileName.endsWith('.html') || chunk.type !== 'asset')
|
|
17
|
+
continue;
|
|
18
|
+
const html = typeof chunk.source === 'string' ? chunk.source : new TextDecoder().decode(chunk.source);
|
|
19
|
+
// 查找 IIFE 脚本块(包含 "Bundled ESM Scripts (IIFE)" 注释的 <script> 标签)
|
|
20
|
+
const iifePattern = /<script>\s*\/\*\s*Bundled ESM Scripts \(IIFE\)\s*\*\/\s*([\s\S]*?)\s*<\/script>/;
|
|
21
|
+
const match = html.match(iifePattern);
|
|
22
|
+
if (!match) {
|
|
23
|
+
console.log('[Obfuscate] 未找到 IIFE 脚本块,跳过混淆');
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
const iifeCode = match[1];
|
|
27
|
+
console.log(`[Obfuscate] 找到 IIFE 脚本块 (${(iifeCode.length / 1024).toFixed(0)} KB),开始混淆...`);
|
|
28
|
+
try {
|
|
29
|
+
const result = await obfuscateAndCompress(iifeCode, options.level);
|
|
30
|
+
// 构建替换内容
|
|
31
|
+
// 如果 fflate 运行时已经通过 compressEngine 注入,则不重复注入
|
|
32
|
+
const runtimeScript = options.fflateRuntimeInjected
|
|
33
|
+
? ''
|
|
34
|
+
: result.runtime;
|
|
35
|
+
// 替换原始 IIFE 块为:fflate runtime(如需) + 自解压 bootstrap
|
|
36
|
+
const replacement = `${runtimeScript}${result.bootstrap}`;
|
|
37
|
+
const newHtml = html.replace(match[0], replacement);
|
|
38
|
+
// 更新 bundle
|
|
39
|
+
chunk.source = newHtml;
|
|
40
|
+
console.log(`[Obfuscate] 混淆完成!`);
|
|
41
|
+
console.log(` 原始: ${(result.originalSize / 1024).toFixed(0)} KB`);
|
|
42
|
+
console.log(` 混淆后: ${(result.obfuscatedSize / 1024).toFixed(0)} KB (+${((result.obfuscatedSize / result.originalSize - 1) * 100).toFixed(0)}%)`);
|
|
43
|
+
console.log(` 压缩后: ${(result.compressedSize / 1024).toFixed(0)} KB (压缩率 ${((1 - result.compressedSize / result.obfuscatedSize) * 100).toFixed(0)}%)`);
|
|
44
|
+
console.log(` 最终: ${(result.finalSize / 1024).toFixed(0)} KB (vs 原始 ${((result.finalSize / result.originalSize * 100)).toFixed(0)}%)`);
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
console.error('[Obfuscate] 混淆失败,保留原始代码:', error);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
}
|
|
@@ -9,6 +9,11 @@ export interface PlatformPluginOptions {
|
|
|
9
9
|
outputFormat: 'html' | 'zip';
|
|
10
10
|
externFiles?: import('../types.js').ExternFilesConfig;
|
|
11
11
|
mraidSupport?: boolean;
|
|
12
|
+
inlineUnsupportedAssets?: boolean;
|
|
13
|
+
/** ZIP 格式输出时,是否对 JS 文件进行 Terser 压缩混淆 */
|
|
14
|
+
minifyJSInZip?: boolean;
|
|
15
|
+
/** Ammo 物理引擎替换方案 */
|
|
16
|
+
ammoReplacement?: 'p2' | 'cannon';
|
|
12
17
|
}
|
|
13
18
|
/**
|
|
14
19
|
* 平台 Vite 插件
|