@playcraft/devkit 1.0.31-beta.11 → 1.0.31-beta.14

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.
Files changed (101) hide show
  1. package/cli/bin/playable-scripts.js +6 -53
  2. package/cli/commands/build.js +32 -241
  3. package/cli/commands/builds.js +2 -13
  4. package/cli/commands/dev.js +1 -22
  5. package/cli/commands/pack.js +40 -320
  6. package/cli/commands/vite-dev.js +1 -7
  7. package/core/artifacts/emit-fonts.test.js +1 -0
  8. package/core/artifacts/emit.js +4 -98
  9. package/core/artifacts/fflate.js +1 -31
  10. package/core/artifacts/zip.js +1 -39
  11. package/core/batch-build.js +147 -1261
  12. package/core/constants.js +2 -20
  13. package/core/cos-uploader.js +23 -289
  14. package/core/engines/cocos/adapter-vfs-path.test.js +1 -0
  15. package/core/engines/cocos/clean-cache.js +5 -179
  16. package/core/engines/cocos/clean-cache.test.js +1 -75
  17. package/core/engines/cocos/cli-build.js +2 -116
  18. package/core/engines/cocos/cli.js +11 -38
  19. package/core/engines/cocos/constants.js +1 -21
  20. package/core/engines/cocos/creator-cli.js +1 -108
  21. package/core/engines/cocos/dev-server.js +4 -412
  22. package/core/engines/cocos/fs.js +1 -105
  23. package/core/engines/cocos/injects/3x/init.js +19 -438
  24. package/core/engines/cocos/injects/3x/main.js +2 -52
  25. package/core/engines/cocos/injects/libs/pako.js +1 -2
  26. package/core/engines/cocos/mcp-refresh.js +2 -76
  27. package/core/engines/cocos/merge-inline.test.js +1 -0
  28. package/core/engines/cocos/merge.js +9 -148
  29. package/core/engines/cocos/omit-playable-weight.js +10 -0
  30. package/core/engines/cocos/omit-playable-weight.test.js +2 -0
  31. package/core/engines/cocos/pack.js +1 -187
  32. package/core/engines/cocos/preview-cache.js +2 -48
  33. package/core/engines/cocos/preview-cache.test.js +1 -32
  34. package/core/engines/cocos/preview-concat.js +6 -189
  35. package/core/engines/cocos/preview-concat.test.js +1 -73
  36. package/core/engines/cocos/preview-json-hook.js +1 -200
  37. package/core/engines/cocos/preview-json-pack.js +3 -376
  38. package/core/engines/cocos/preview-json-pack.test.js +2 -226
  39. package/core/engines/cocos/preview-proxy.js +1 -132
  40. package/core/engines/cocos/rebundle-preview.js +2 -54
  41. package/core/engines/cocos/strip-social-share.js +3 -115
  42. package/core/engines/cocos/theme-assets.js +2 -161
  43. package/core/engines/cocos/theme-levels.js +1 -0
  44. package/core/engines/cocos/writable-cli.js +2 -248
  45. package/core/engines/cocos/writable-cli.test.js +1 -112
  46. package/core/engines/web/index.js +1 -20
  47. package/core/engines/web/vite.dev.js +17 -322
  48. package/core/engines/web/webpack.build.js +37 -522
  49. package/core/engines/web/webpack.common.js +11 -177
  50. package/core/engines/web/webpack.dev.js +6 -88
  51. package/core/index.js +7 -101
  52. package/core/injectors/apply.js +1 -94
  53. package/core/injectors/check-channel-matrix.js +2 -130
  54. package/core/injectors/spec.js +10 -217
  55. package/core/loaders/gltf-loader.js +28 -74
  56. package/core/options.js +16 -353
  57. package/core/plugins/AdikteevInjectorPlugin.js +4 -26
  58. package/core/plugins/BigoAdsInjectorPlugin.js +4 -21
  59. package/core/plugins/DAPIInjectorPlugin.js +2 -25
  60. package/core/plugins/DebuggerInjectionPlugin.js +3 -31
  61. package/core/plugins/ExitAPIInjectorPlugin.js +3 -56
  62. package/core/plugins/FflateCompressionPlugin.js +160 -307
  63. package/core/plugins/FflateCompressionPlugin.test.js +13 -0
  64. package/core/plugins/FontInjectionWebpackPlugin.js +4 -46
  65. package/core/plugins/LiftoffInjectorPlugin.js +4 -26
  66. package/core/plugins/MRAIDInjectorPlugin.js +2 -24
  67. package/core/plugins/MintegralInjectorPlugin.js +2 -25
  68. package/core/plugins/PangleInjectorPlugin.js +2 -24
  69. package/core/plugins/PlaycraftOrientationInjectorPlugin.js +3 -29
  70. package/core/plugins/SnapchatInjectorPlugin.js +4 -26
  71. package/core/plugins/SocialMediaInjectorPlugin.js +7 -119
  72. package/core/plugins/TikTokInjectorPlugin.js +2 -24
  73. package/core/plugins/UnityInjectorPlugin.js +8 -37
  74. package/core/plugins/ZipPlugin.js +20 -154
  75. package/core/plugins/socialMediaVitePlugin.js +2 -108
  76. package/core/utils/buildDefines.js +2 -30
  77. package/core/utils/buildTemplateString.js +5 -39
  78. package/core/utils/date.js +1 -16
  79. package/core/utils/fontRegistry.js +13 -81
  80. package/core/utils/fontResolver.js +37 -335
  81. package/core/utils/generateAdikteevHtmlWebpackPluginConfig.js +8 -42
  82. package/core/utils/generateBigabidHtmlWebpackPluginConfig.js +3 -30
  83. package/core/utils/generateInMobiHtmlWebpackPluginConfig.js +7 -43
  84. package/core/utils/getPlaycraftOrientationSuffix.js +2 -9
  85. package/core/utils/injectSDKPlugins.js +13 -68
  86. package/core/utils/logOptions.js +5 -46
  87. package/core/utils/mergeOptions.js +2 -21
  88. package/core/utils/obfuscateLevels.js +14 -0
  89. package/core/utils/obfuscateLevels.test.js +10 -0
  90. package/core/utils/parseArgvOptions.js +5 -125
  91. package/core/utils/resolveChannelFold2Zip.js +3 -15
  92. package/core/utils/validateThemeData.js +26 -227
  93. package/core/validators/pre-build-checker.js +25 -287
  94. package/core/validators/tracking-validator.js +40 -441
  95. package/core/validators/tracking-validator.test.js +2 -84
  96. package/core/vite.dev.js +1 -1
  97. package/core/webpack.build.js +1 -1
  98. package/core/webpack.common.js +1 -1
  99. package/core/webpack.dev.js +1 -1
  100. package/package.json +2 -3
  101. package/README.md +0 -262
@@ -1,22 +1,7 @@
1
- /**
1
+ "use strict";/**
2
2
  * 根据 AD_NETWORK 和 AD_PROTOCOL 注入对应的渠道 SDK 脚本插件
3
3
  * 此函数被 webpack.dev.js 和 webpack.build.js 共用
4
- */
5
-
6
- const { ExitAPIInjectorPlugin } = require('../plugins/ExitAPIInjectorPlugin.js');
7
- const { DAPIInjectorPlugin } = require('../plugins/DAPIInjectorPlugin.js');
8
- const { PangleInjectorPlugin } = require('../plugins/PangleInjectorPlugin.js');
9
- const { TikTokInjectorPlugin } = require('../plugins/TikTokInjectorPlugin.js');
10
- const { MintegralInjectorPlugin } = require('../plugins/MintegralInjectorPlugin.js');
11
- const { MRAIDInjectorPlugin } = require('../plugins/MRAIDInjectorPlugin.js');
12
- const { BigoAdsInjectorPlugin } = require('../plugins/BigoAdsInjectorPlugin.js');
13
- const { LiftoffInjectorPlugin } = require('../plugins/LiftoffInjectorPlugin.js');
14
- const { SnapchatInjectorPlugin } = require('../plugins/SnapchatInjectorPlugin.js');
15
- const { AdikteevInjectorPlugin } = require('../plugins/AdikteevInjectorPlugin.js');
16
- const { UnityInjectorPlugin } = require('../plugins/UnityInjectorPlugin.js');
17
- const { SocialMediaInjectorPlugin } = require('../plugins/SocialMediaInjectorPlugin.js');
18
-
19
- /**
4
+ */const{ExitAPIInjectorPlugin}=require("../plugins/ExitAPIInjectorPlugin.js");const{DAPIInjectorPlugin}=require("../plugins/DAPIInjectorPlugin.js");const{PangleInjectorPlugin}=require("../plugins/PangleInjectorPlugin.js");const{TikTokInjectorPlugin}=require("../plugins/TikTokInjectorPlugin.js");const{MintegralInjectorPlugin}=require("../plugins/MintegralInjectorPlugin.js");const{MRAIDInjectorPlugin}=require("../plugins/MRAIDInjectorPlugin.js");const{BigoAdsInjectorPlugin}=require("../plugins/BigoAdsInjectorPlugin.js");const{LiftoffInjectorPlugin}=require("../plugins/LiftoffInjectorPlugin.js");const{SnapchatInjectorPlugin}=require("../plugins/SnapchatInjectorPlugin.js");const{AdikteevInjectorPlugin}=require("../plugins/AdikteevInjectorPlugin.js");const{UnityInjectorPlugin}=require("../plugins/UnityInjectorPlugin.js");const{SocialMediaInjectorPlugin}=require("../plugins/SocialMediaInjectorPlugin.js");/**
20
5
  * 注入渠道 SDK 插件到 webpack 配置中
21
6
  * @param {import('webpack').Configuration} webpackConfig - webpack 配置对象
22
7
  * @param {Object} options - 配置选项
@@ -27,54 +12,14 @@ const { SocialMediaInjectorPlugin } = require('../plugins/SocialMediaInjectorPlu
27
12
  * @param {string} [options.themeName] - 当前主题名
28
13
  * @param {object} [options.themeConfig] - builds.config.js 中的 themes 配置
29
14
  * @param {object} [options.mergedData] - 预合并的主题数据(可选)
30
- */
31
- function injectSDKPlugins(webpackConfig, options) {
32
- const { network: adNetwork, protocol: adProtocol, playcraftOrientationValue, projectRoot, themeName, themeConfig, mergedData } = options;
33
-
34
- // 协议层注入
35
- if ('dapi' === adProtocol) {
36
- webpackConfig.plugins.push(new DAPIInjectorPlugin());
37
- } else if ('mraid' === adProtocol) {
38
- // ironsource / remerge:HTML 内必须带 <script src="mraid.js">
39
- // Unity 使用专门的 UnityInjectorPlugin(在渠道特定注入中处理)
40
- if (adNetwork === 'ironsource' || adNetwork === 'remerge') {
41
- webpackConfig.plugins.push(new MRAIDInjectorPlugin());
42
- }
43
- }
44
-
45
- // 渠道特定注入
46
- if (adNetwork === 'google') {
47
- webpackConfig.plugins.push(new ExitAPIInjectorPlugin(playcraftOrientationValue));
48
- } else if (adNetwork === 'unity') {
49
- // Unity Ads 使用专门的 UnityInjectorPlugin
50
- webpackConfig.plugins.push(new UnityInjectorPlugin());
51
- } else if (adNetwork === 'pangle') {
52
- webpackConfig.plugins.push(new PangleInjectorPlugin());
53
- } else if (adNetwork === 'tiktok') {
54
- webpackConfig.plugins.push(new TikTokInjectorPlugin());
55
- } else if (adNetwork === 'mintegral') {
56
- webpackConfig.plugins.push(new MintegralInjectorPlugin());
57
- } else if (adNetwork === 'bigoads') {
58
- // BigoAds 在 dev 模式下注入模拟配置
59
- webpackConfig.plugins.push(new BigoAdsInjectorPlugin());
60
- } else if (adNetwork === 'liftoff') {
61
- webpackConfig.plugins.push(new LiftoffInjectorPlugin());
62
- } else if (adNetwork === 'snapchat') {
63
- // Snapchat 注入 ScPlayableAd SDK
64
- webpackConfig.plugins.push(new SnapchatInjectorPlugin());
65
- } else if (adNetwork === 'adikteev') {
66
- // Adikteev 注入 MRAID mock 和占位符变量
67
- webpackConfig.plugins.push(new AdikteevInjectorPlugin());
68
- } else if (adNetwork === 'social_media') {
69
- // social_media 渠道:注入 OG / Twitter meta 标签,让 Facebook / LinkedIn 爬取时
70
- // 能拿到正确的标题/描述/预览图。注意 Telegram / Slack / Discord 也读 OG meta
71
- webpackConfig.plugins.push(new SocialMediaInjectorPlugin({
72
- projectRoot,
73
- themeName,
74
- themeConfig,
75
- mergedData,
76
- }));
77
- }
78
- }
79
-
80
- exports.injectSDKPlugins = injectSDKPlugins;
15
+ */function injectSDKPlugins(webpackConfig,options){const{network:adNetwork,protocol:adProtocol,playcraftOrientationValue,projectRoot,themeName,themeConfig,mergedData}=options;// 协议层注入
16
+ if("dapi"===adProtocol){webpackConfig.plugins.push(new DAPIInjectorPlugin)}else if("mraid"===adProtocol){// ironsource / remerge:HTML 内必须带 <script src="mraid.js">
17
+ // Unity 使用专门的 UnityInjectorPlugin(在渠道特定注入中处理)
18
+ if(adNetwork==="ironsource"||adNetwork==="remerge"){webpackConfig.plugins.push(new MRAIDInjectorPlugin)}}// 渠道特定注入
19
+ if(adNetwork==="google"){webpackConfig.plugins.push(new ExitAPIInjectorPlugin(playcraftOrientationValue))}else if(adNetwork==="unity"){// Unity Ads 使用专门的 UnityInjectorPlugin
20
+ webpackConfig.plugins.push(new UnityInjectorPlugin)}else if(adNetwork==="pangle"){webpackConfig.plugins.push(new PangleInjectorPlugin)}else if(adNetwork==="tiktok"){webpackConfig.plugins.push(new TikTokInjectorPlugin)}else if(adNetwork==="mintegral"){webpackConfig.plugins.push(new MintegralInjectorPlugin)}else if(adNetwork==="bigoads"){// BigoAds 在 dev 模式下注入模拟配置
21
+ webpackConfig.plugins.push(new BigoAdsInjectorPlugin)}else if(adNetwork==="liftoff"){webpackConfig.plugins.push(new LiftoffInjectorPlugin)}else if(adNetwork==="snapchat"){// Snapchat 注入 ScPlayableAd SDK
22
+ webpackConfig.plugins.push(new SnapchatInjectorPlugin)}else if(adNetwork==="adikteev"){// Adikteev 注入 MRAID mock 和占位符变量
23
+ webpackConfig.plugins.push(new AdikteevInjectorPlugin)}else if(adNetwork==="social_media"){// social_media 渠道:注入 OG / Twitter meta 标签,让 Facebook / LinkedIn 爬取时
24
+ // 能拿到正确的标题/描述/预览图。注意 Telegram / Slack / Discord 也读 OG meta
25
+ webpackConfig.plugins.push(new SocialMediaInjectorPlugin({projectRoot,themeName,themeConfig,mergedData}))}}exports.injectSDKPlugins=injectSDKPlugins;
@@ -1,47 +1,6 @@
1
- var prettyjson = require('prettyjson');
2
-
3
- /**
1
+ "use strict";var prettyjson=require("prettyjson");/**
4
2
  * Log provided options into console output
5
- */
6
- exports.logOptions = function logOptions(options) {
7
- let logOptions = { mode: process.env.NODE_ENV, ...options };
8
- let isProduction = false;
9
- if (process.env.NODE_ENV === 'production') {
10
- isProduction = true;
11
- delete logOptions.port;
12
- delete logOptions.open;
13
- } else if (process.env.NODE_ENV === 'development') {
14
- delete logOptions.outDir;
15
- }
16
-
17
- if (Object.keys(logOptions.compilation).length === 0 || !isProduction) delete logOptions.compilation;
18
- if (Object.keys(logOptions.adNetworkNames).length === 0 || !isProduction) delete logOptions.adNetworkNames;
19
- if (Object.keys(logOptions.defines).length === 0) delete logOptions.defines;
20
- if (logOptions.zip === false || !isProduction) delete logOptions.zip;
21
- if (logOptions.tsConfig === 'tsconfig.json') delete logOptions.tsConfig;
22
- if (logOptions.jsConfig === 'jsconfig.json') delete logOptions.jsConfig;
23
- if (logOptions.buildConfig === 'build.json') delete logOptions.buildConfig;
24
- if (logOptions.language === 'en') delete logOptions.language;
25
- // playcraftOrientation/playcraftOrientationChannel 为默认值时不显示,避免日志噪音
26
- if (Array.isArray(logOptions.playcraftOrientation) && logOptions.playcraftOrientation.length === 1 && logOptions.playcraftOrientation[0] === 'both') {
27
- delete logOptions.playcraftOrientation;
28
- }
29
- if (Array.isArray(logOptions.playcraftOrientationChannel) && logOptions.playcraftOrientationChannel.length === 0) {
30
- delete logOptions.playcraftOrientationChannel;
31
- }
32
- if (!logOptions.playcraftOrientationValue || logOptions.playcraftOrientationValue === 'both') {
33
- delete logOptions.playcraftOrientationValue;
34
- }
35
- // 在 dev 模式下,如果 network 为默认值 preview 则不显示
36
- if (!isProduction && logOptions.network === 'preview') delete logOptions.network;
37
- // 在 dev 模式下,如果 protocol 为默认值 none 则不显示
38
- if (!isProduction && logOptions.protocol === 'none') delete logOptions.protocol;
39
- delete logOptions.filename;
40
- delete logOptions.app;
41
- delete logOptions.name;
42
- delete logOptions.googlePlayUrl;
43
- delete logOptions.appStoreUrl;
44
- if (logOptions.obfuscateLevel === 2) delete logOptions.obfuscateLevel;
45
-
46
- console.log(prettyjson.render(logOptions, {}, 2));
47
- };
3
+ */exports.logOptions=function logOptions(options){let logOptions={mode:process.env.NODE_ENV,...options};let isProduction=false;if(process.env.NODE_ENV==="production"){isProduction=true;delete logOptions.port;delete logOptions.open}else if(process.env.NODE_ENV==="development"){delete logOptions.outDir}if(Object.keys(logOptions.compilation).length===0||!isProduction)delete logOptions.compilation;if(Object.keys(logOptions.adNetworkNames).length===0||!isProduction)delete logOptions.adNetworkNames;if(Object.keys(logOptions.defines).length===0)delete logOptions.defines;if(logOptions.zip===false||!isProduction)delete logOptions.zip;if(logOptions.tsConfig==="tsconfig.json")delete logOptions.tsConfig;if(logOptions.jsConfig==="jsconfig.json")delete logOptions.jsConfig;if(logOptions.buildConfig==="build.json")delete logOptions.buildConfig;if(logOptions.language==="en")delete logOptions.language;// playcraftOrientation/playcraftOrientationChannel 为默认值时不显示,避免日志噪音
4
+ if(Array.isArray(logOptions.playcraftOrientation)&&logOptions.playcraftOrientation.length===1&&logOptions.playcraftOrientation[0]==="both"){delete logOptions.playcraftOrientation}if(Array.isArray(logOptions.playcraftOrientationChannel)&&logOptions.playcraftOrientationChannel.length===0){delete logOptions.playcraftOrientationChannel}if(!logOptions.playcraftOrientationValue||logOptions.playcraftOrientationValue==="both"){delete logOptions.playcraftOrientationValue}// 在 dev 模式下,如果 network 为默认值 preview 则不显示
5
+ if(!isProduction&&logOptions.network==="preview")delete logOptions.network;// 在 dev 模式下,如果 protocol 为默认值 none 则不显示
6
+ if(!isProduction&&logOptions.protocol==="none")delete logOptions.protocol;delete logOptions.filename;delete logOptions.app;delete logOptions.name;delete logOptions.googlePlayUrl;delete logOptions.appStoreUrl;if(logOptions.obfuscateLevel===2)delete logOptions.obfuscateLevel;console.log(prettyjson.render(logOptions,{},2))};
@@ -1,25 +1,6 @@
1
- /**
1
+ "use strict";/**
2
2
  * Merges two option objects with deep merge for 'defines' and 'build' properties
3
3
  * @param {import('../index').CLIOptions} options1 - First options object
4
4
  * @param {Partial<import('../index').CLIOptions>} [options2] - Second options object
5
5
  * @returns {import('../index').CLIOptions} Merged options object with combined defines and build properties
6
- */
7
- exports.mergeOptions = function mergeOptions(options1, options2) {
8
- if (!options1) return {};
9
- if (!options2) return options1;
10
-
11
- const defines = Object.assign({}, options1.defines, options2.defines);
12
- const build = Object.assign({}, options1.build, options2.build);
13
- const compilation = Object.assign({}, options1.compilation, options2.compilation);
14
- const adNetworkNames = Object.assign({}, options1.adNetworkNames, options2.adNetworkNames);
15
- const cocos = Object.assign({}, options1.cocos, options2.cocos);
16
- const options = Object.assign({}, options1, options2);
17
-
18
- options.defines = defines;
19
- options.compilation = compilation;
20
- options.adNetworkNames = adNetworkNames;
21
- options.build = build;
22
- options.cocos = cocos;
23
-
24
- return options;
25
- };
6
+ */exports.mergeOptions=function mergeOptions(options1,options2){if(!options1)return{};if(!options2)return options1;const defines=Object.assign({},options1.defines,options2.defines);const build=Object.assign({},options1.build,options2.build);const compilation=Object.assign({},options1.compilation,options2.compilation);const adNetworkNames=Object.assign({},options1.adNetworkNames,options2.adNetworkNames);const cocos=Object.assign({},options1.cocos,options2.cocos);const options=Object.assign({},options1,options2);options.defines=defines;options.compilation=compilation;options.adNetworkNames=adNetworkNames;options.build=build;options.cocos=cocos;return options};
@@ -0,0 +1,14 @@
1
+ "use strict";/**
2
+ * Same ladder as the Phaser webpack build:
3
+ * 0 none, 1 terser, 2 basic obfuscator, 3 + dead code, 4 maximum.
4
+ * Cocos applies this only to business bundles (assets/main/index.js).
5
+ * Order matches webpack: obfuscator loader, then Terser minimizer.
6
+ */const LEVEL_NAMES=["none","terser-only","basic","dead-code","maximum"];function obfuscateLevelName(level){const n=Number(level)||0;return LEVEL_NAMES[n]||LEVEL_NAMES[0]}function obfuscatorOptionsForLevel(level){const n=Number(level)||0;if(n<2)return null;const options={target:"browser",sourceMap:false,selfDefending:false,renameProperties:false,disableConsoleOutput:true,splitStrings:false,unicodeEscapeSequence:false};if(n===2){Object.assign(options,{controlFlowFlattening:true,controlFlowFlatteningThreshold:0.75,deadCodeInjection:false,stringArray:true,stringArrayThreshold:0.75,stringArrayEncoding:["base64"],stringArrayIndexShift:true,stringArrayRotate:true,stringArrayShuffle:true,stringArrayWrappersCount:2,stringArrayWrappersChainedCalls:true,stringArrayWrappersType:"function",stringArrayCallsTransform:true,stringArrayCallsTransformThreshold:0.75,transformObjectKeys:true,identifierNamesGenerator:"hexadecimal"})}else if(n===3){Object.assign(options,{controlFlowFlattening:true,controlFlowFlatteningThreshold:0.75,deadCodeInjection:true,deadCodeInjectionThreshold:0.4,stringArray:true,stringArrayThreshold:0.75,stringArrayEncoding:["base64"],stringArrayIndexShift:true,stringArrayRotate:true,stringArrayShuffle:true,stringArrayWrappersCount:2,stringArrayWrappersChainedCalls:true,stringArrayWrappersType:"function",stringArrayCallsTransform:true,stringArrayCallsTransformThreshold:0.75,transformObjectKeys:true,identifierNamesGenerator:"hexadecimal"})}else{Object.assign(options,{controlFlowFlattening:true,controlFlowFlatteningThreshold:1,deadCodeInjection:true,deadCodeInjectionThreshold:0.5,stringArray:true,stringArrayThreshold:1,stringArrayEncoding:["rc4"],stringArrayIndexShift:true,stringArrayRotate:true,stringArrayShuffle:true,stringArrayWrappersCount:3,stringArrayWrappersChainedCalls:true,stringArrayWrappersType:"function",stringArrayCallsTransform:true,stringArrayCallsTransformThreshold:1,transformObjectKeys:true,identifierNamesGenerator:"hexadecimal",splitStrings:true,splitStringsChunkLength:5,unicodeEscapeSequence:true})}return options}/**
7
+ * Cocos obfuscates one already-minified main bundle, not per-source files.
8
+ * Phaser's level 4 (RC4 + control-flow 1 + splitStrings) does not finish on that file.
9
+ * Keep stringArrayThreshold at 1 so literals are covered, and drop the options that hang.
10
+ */function obfuscatorOptionsForCocos(level){const options=obfuscatorOptionsForLevel(level);if(!options||Number(level)<4)return options;return{...options,stringArrayEncoding:["base64"],stringArrayThreshold:1,controlFlowFlattening:true,controlFlowFlatteningThreshold:0.75,deadCodeInjection:true,deadCodeInjectionThreshold:0.4,stringArrayWrappersCount:2,stringArrayCallsTransformThreshold:0.75,splitStrings:false,unicodeEscapeSequence:false}}function terserOptions(){return{safari10:true,mangle:{reserved:["$"],toplevel:true},compress:{drop_console:true,arrows:false,drop_debugger:true,unused:true,dead_code:true,passes:3,inline:3,collapse_vars:true,reduce_vars:true,hoist_vars:true,hoist_funs:true,sequences:20,properties:true,conditionals:true,comparisons:true,booleans:true,loops:true,if_return:true,join_vars:true,negate_iife:true,side_effects:true,switches:true,typeofs:true,unsafe:false,keep_fargs:false},output:{comments:false,quote_style:3,beautify:false,wrap_iife:true,ecma:2015},toplevel:true,keep_classnames:false,keep_fnames:false}}/**
11
+ * Creator web-mobile gameplay bundle. Engine (`cocos-js`, `assets/internal`)
12
+ * and the resources asset table stay as Creator emitted them.
13
+ * @param {string} objKey path inside the web-mobile folder
14
+ */function isCocosBusinessScript(objKey){const key=String(objKey||"").replace(/\\/g,"/").replace(/^\//,"");return /^assets\/main\/index(?:\.[^/]+)?\.js$/.test(key)}async function applyObfuscation(code,level){const n=Number(level)||0;if(n<=0||typeof code!=="string"||!code)return code;let out=code;const obf=obfuscatorOptionsForCocos(n);if(obf){if(n>=4){console.log("[cocos] level 4 on the main bundle uses base64 string arrays; RC4 and full control-flow do not finish")}const JavaScriptObfuscator=require("javascript-obfuscator");out=JavaScriptObfuscator.obfuscate(out,obf).getObfuscatedCode()}if(n>=1){const{minify}=require("terser");const result=await minify(out,terserOptions());if(!result||!result.code){throw new Error(`terser produced empty output at obfuscate level ${n}`)}out=result.code}return out}module.exports={LEVEL_NAMES,obfuscateLevelName,obfuscatorOptionsForLevel,obfuscatorOptionsForCocos,terserOptions,isCocosBusinessScript,applyObfuscation};
@@ -0,0 +1,10 @@
1
+ "use strict";const{describe,it}=require("node:test");const assert=require("node:assert/strict");const vm=require("vm");const{isCocosBusinessScript,applyObfuscation,obfuscatorOptionsForLevel,obfuscatorOptionsForCocos}=require("./obfuscateLevels.js");const SAMPLE=`
2
+ System.register("chunks:///_virtual/Demo.js", ["./rollupPluginModLoBabelHelpers.js", "cc"], function (exports) {
3
+ return {
4
+ setters: [function () {}, function () {}],
5
+ execute: function () {
6
+ exports("findPath", function (start, goal) { return start + goal; });
7
+ }
8
+ };
9
+ });
10
+ `;function loadRegisters(code){const registered=[];const context=vm.createContext({System:{register(name,deps,factory){const ns=factory(function(){});registered.push({name:String(name),deps:Array.from(deps,dep=>String(dep)),keys:ns&&typeof ns==="object"?Object.keys(ns).map(String).sort():[]})}},console});vm.runInContext(code,context,{timeout:5000});return registered}describe("isCocosBusinessScript",()=>{it("matches the main bundle only",()=>{assert.equal(isCocosBusinessScript("assets/main/index.js"),true);assert.equal(isCocosBusinessScript("assets/main/index.abc123.js"),true);assert.equal(isCocosBusinessScript("assets/internal/index.js"),false);assert.equal(isCocosBusinessScript("assets/resources/index.js"),false);assert.equal(isCocosBusinessScript("cocos-js/cc.js"),false)})});describe("applyObfuscation",()=>{it("keeps System.register setters and execute at every level",async()=>{const original=loadRegisters(SAMPLE);assert.deepEqual(original[0].keys,["execute","setters"]);for(const level of[1,2,3,4]){const out=await applyObfuscation(SAMPLE,level);assert.equal(typeof out,"string");assert.ok(out.length>0,`level ${level} empty`);const registered=loadRegisters(out);assert.equal(registered.length,1,`level ${level} register count`);assert.deepEqual(registered[0].keys,["execute","setters"],`level ${level} keys`);assert.deepEqual(registered[0].deps,original[0].deps,`level ${level} deps`)}});it("level 0 and 1 do not enable the obfuscator",()=>{assert.equal(obfuscatorOptionsForLevel(0),null);assert.equal(obfuscatorOptionsForLevel(1),null);assert.equal(obfuscatorOptionsForLevel(2).stringArray,true);assert.equal(obfuscatorOptionsForLevel(3).deadCodeInjection,true);assert.equal(obfuscatorOptionsForLevel(4).stringArrayEncoding[0],"rc4");assert.equal(obfuscatorOptionsForCocos(4).stringArrayEncoding[0],"base64");assert.equal(obfuscatorOptionsForCocos(4).stringArrayThreshold,1);assert.equal(obfuscatorOptionsForCocos(4).splitStrings,false)})});
@@ -1,128 +1,8 @@
1
- /** @type {string[]} List of supported advertising networks */
2
- const allowedAdNetworks = [
3
- 'preview',
4
- 'applovin',
5
- 'unity',
6
- 'google',
7
- 'ironsource',
8
- 'facebook',
9
- 'moloco',
10
- 'adcolony',
11
- 'mintegral',
12
- 'vungle',
13
- 'tapjoy',
14
- 'snapchat',
15
- 'tiktok',
16
- 'appreciate',
17
- 'chartboost',
18
- 'pangle',
19
- 'mytarget',
20
- 'liftoff',
21
- 'smadex',
22
- 'adikteev',
23
- 'bigabid',
24
- 'inmobi',
25
- 'bigoads',
26
- 'remerge',
27
- 'social_media'
28
- ];
29
- /** @type {AD_PROTOCOL[]} List of supported advertising protocols */
30
- const allowedAdProtocols = ['none', 'mraid', 'dapi'];
31
-
32
- /** @type {AD_NETWORK[]} List of advertising partners that support MRAID protocol */
33
- const mraidPartners = [
34
- 'ironsource',
35
- 'applovin',
36
- 'unity',
37
- 'appreciate',
38
- 'chartboost',
39
- 'mytarget',
40
- 'liftoff',
41
- 'adcolony',
42
- 'adikteev',
43
- 'bigabid',
44
- 'inmobi',
45
- 'remerge'
46
- ];
47
-
48
- /** @type {LANGUAGE[]} */
49
- const allowedLanguages = ['auto', 'en', 'es', 'zh', 'hi', 'ar', 'fr', 'de', 'ja', 'pt'];
50
-
51
- /** @type {('both'|'landscape'|'portrait')[]} playcraftOrientation 允许的横竖屏方向版本取值 */
52
- const allowedPlaycraftOrientations = ['both', 'landscape', 'portrait'];
53
-
54
- /** @type {('phaser'|'threejs'|'cocos')[]} */
55
- const allowedEngines = ['phaser', 'threejs', 'cocos'];
56
-
57
- const WEB_ENGINES = ['phaser', 'threejs'];
58
-
59
- function isWebEngine(engine) {
60
- return !engine || WEB_ENGINES.includes(engine);
61
- }
62
-
63
- /**
1
+ "use strict";/** @type {string[]} List of supported advertising networks */const allowedAdNetworks=["preview","applovin","unity","google","ironsource","facebook","moloco","adcolony","mintegral","vungle","tapjoy","snapchat","tiktok","appreciate","chartboost","pangle","mytarget","liftoff","smadex","adikteev","bigabid","inmobi","bigoads","remerge","social_media"];/** @type {AD_PROTOCOL[]} List of supported advertising protocols */const allowedAdProtocols=["none","mraid","dapi"];/** @type {AD_NETWORK[]} List of advertising partners that support MRAID protocol */const mraidPartners=["ironsource","applovin","unity","appreciate","chartboost","mytarget","liftoff","adcolony","adikteev","bigabid","inmobi","remerge"];/** @type {LANGUAGE[]} */const allowedLanguages=["auto","en","es","zh","hi","ar","fr","de","ja","pt"];/** @type {('both'|'landscape'|'portrait')[]} playcraftOrientation 允许的横竖屏方向版本取值 */const allowedPlaycraftOrientations=["both","landscape","portrait"];/** @type {('phaser'|'threejs'|'cocos')[]} */const allowedEngines=["phaser","threejs","cocos"];const WEB_ENGINES=["phaser","threejs"];function isWebEngine(engine){return!engine||WEB_ENGINES.includes(engine)}/**
64
2
  * Parses command line arguments based on provided options configuration
65
3
  * @param {import('../index').CLIOptionConfig[]} posiibleOptions - Array of possible options to parse
66
4
  * @returns {Record<string, any>} Parsed options object with values from command line arguments
67
- */
68
- exports.parseArgvOptions = function parseArgvOptions(posiibleOptions) {
69
- const argvOptions = {};
70
- // Track which options were explicitly set via CLI args
71
- const cliExplicitKeys = new Set();
72
-
73
- for (let possibleOption of posiibleOptions) {
74
- if (possibleOption.defaultValue !== undefined)
75
- argvOptions[possibleOption.alias || possibleOption.name] = possibleOption.defaultValue;
76
- }
77
-
78
- let adNetwork = null;
79
-
80
- for (let i = 2; i < process.argv.length; i++) {
81
- let key = process.argv[i];
82
- let inlineValue = null;
83
-
84
- // 支持 --key=value 格式
85
- if (key.includes('=')) {
86
- const eqIndex = key.indexOf('=');
87
- inlineValue = key.slice(eqIndex + 1);
88
- key = key.slice(0, eqIndex);
89
- }
90
-
91
- const allowedOption = posiibleOptions.find((e) => '--' + e.name === key);
92
-
93
- if (allowedOption) {
94
- let value = true;
95
- if (allowedOption.hasValue) {
96
- // 优先使用内联值(--key=value),否则使用下一个参数
97
- value = inlineValue !== null ? inlineValue : process.argv[++i];
98
- if (allowedOption.parser) value = allowedOption.parser(value);
99
- }
100
- const optionKey = allowedOption.alias || allowedOption.name;
101
- argvOptions[optionKey] = value;
102
- cliExplicitKeys.add(optionKey);
103
- } else if (!adNetwork) adNetwork = key;
104
- }
105
-
106
- if (posiibleOptions.find((e) => e.name === 'network')) {
107
- if (adNetwork && allowedAdNetworks.includes(adNetwork)) {
108
- argvOptions['network'] = adNetwork;
109
- }
110
- if (posiibleOptions.find((e) => e.name === 'protocol')) {
111
- if (mraidPartners.includes(argvOptions['network']) && argvOptions['protocol'] === 'none') {
112
- argvOptions['protocol'] = 'mraid';
113
- }
114
- }
115
- }
116
-
117
- argvOptions._cliExplicitKeys = cliExplicitKeys;
118
- return argvOptions;
119
- };
120
-
121
- exports.allowedAdNetworks = allowedAdNetworks;
122
- exports.allowedAdProtocols = allowedAdProtocols;
123
- exports.mraidPartners = mraidPartners;
124
- exports.allowedLanguages = allowedLanguages;
125
- exports.allowedPlaycraftOrientations = allowedPlaycraftOrientations;
126
- exports.allowedEngines = allowedEngines;
127
- exports.WEB_ENGINES = WEB_ENGINES;
128
- exports.isWebEngine = isWebEngine;
5
+ */exports.parseArgvOptions=function parseArgvOptions(posiibleOptions){const argvOptions={};// Track which options were explicitly set via CLI args
6
+ const cliExplicitKeys=new Set;for(let possibleOption of posiibleOptions){if(possibleOption.defaultValue!==undefined)argvOptions[possibleOption.alias||possibleOption.name]=possibleOption.defaultValue}let adNetwork=null;for(let i=2;i<process.argv.length;i++){let key=process.argv[i];let inlineValue=null;// 支持 --key=value 格式
7
+ if(key.includes("=")){const eqIndex=key.indexOf("=");inlineValue=key.slice(eqIndex+1);key=key.slice(0,eqIndex)}const allowedOption=posiibleOptions.find(e=>"--"+e.name===key);if(allowedOption){let value=true;if(allowedOption.hasValue){// 优先使用内联值(--key=value),否则使用下一个参数
8
+ value=inlineValue!==null?inlineValue:process.argv[++i];if(allowedOption.parser)value=allowedOption.parser(value)}const optionKey=allowedOption.alias||allowedOption.name;argvOptions[optionKey]=value;cliExplicitKeys.add(optionKey)}else if(!adNetwork)adNetwork=key}if(posiibleOptions.find(e=>e.name==="network")){if(adNetwork&&allowedAdNetworks.includes(adNetwork)){argvOptions["network"]=adNetwork}if(posiibleOptions.find(e=>e.name==="protocol")){if(mraidPartners.includes(argvOptions["network"])&&argvOptions["protocol"]==="none"){argvOptions["protocol"]="mraid"}}}argvOptions._cliExplicitKeys=cliExplicitKeys;return argvOptions};exports.allowedAdNetworks=allowedAdNetworks;exports.allowedAdProtocols=allowedAdProtocols;exports.mraidPartners=mraidPartners;exports.allowedLanguages=allowedLanguages;exports.allowedPlaycraftOrientations=allowedPlaycraftOrientations;exports.allowedEngines=allowedEngines;exports.WEB_ENGINES=WEB_ENGINES;exports.isWebEngine=isWebEngine;
@@ -1,4 +1,4 @@
1
- /**
1
+ "use strict";/**
2
2
  * 解析 isChannelFold2Zip 相关标志
3
3
  *
4
4
  * isChannelFold2Zip:所有渠道(排除 preview)输出 dist/渠道名称.zip
@@ -10,17 +10,5 @@
10
10
  * @param {string} params.adNetwork - 当前渠道名称
11
11
  * @param {boolean} params.isZipOutput - 当前渠道是否本身就走 ZIP 输出流程
12
12
  * @returns {{ isChannelFold2Zip: boolean, isZipOutputFinal: boolean }}
13
- */
14
- function resolveChannelFold2Zip({ isChannelFold2ZipOption, adNetwork, isZipOutput }) {
15
- const isChannelFold2Zip = !!(isChannelFold2ZipOption && adNetwork !== 'preview');
16
- // 非 ZIP 渠道启用 isChannelFold2Zip 时,需要强制走 ZIP 输出流程
17
- const isZipOutputFinal = isZipOutput || isChannelFold2Zip;
18
-
19
- if (isChannelFold2Zip) {
20
- console.log(`✅ isChannelFold2Zip enabled: output will be dist/${adNetwork}.zip`);
21
- }
22
-
23
- return { isChannelFold2Zip, isZipOutputFinal };
24
- }
25
-
26
- exports.resolveChannelFold2Zip = resolveChannelFold2Zip;
13
+ */function resolveChannelFold2Zip({isChannelFold2ZipOption,adNetwork,isZipOutput}){const isChannelFold2Zip=!!(isChannelFold2ZipOption&&adNetwork!=="preview");// 非 ZIP 渠道启用 isChannelFold2Zip 时,需要强制走 ZIP 输出流程
14
+ const isZipOutputFinal=isZipOutput||isChannelFold2Zip;if(isChannelFold2Zip){console.log(`✅ isChannelFold2Zip enabled: output will be dist/${adNetwork}.zip`)}return{isChannelFold2Zip,isZipOutputFinal}}exports.resolveChannelFold2Zip=resolveChannelFold2Zip;