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

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 (95) 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/clean-cache.js +5 -179
  15. package/core/engines/cocos/clean-cache.test.js +1 -75
  16. package/core/engines/cocos/cli-build.js +2 -116
  17. package/core/engines/cocos/cli.js +11 -38
  18. package/core/engines/cocos/constants.js +1 -21
  19. package/core/engines/cocos/creator-cli.js +1 -108
  20. package/core/engines/cocos/dev-server.js +4 -412
  21. package/core/engines/cocos/fs.js +1 -105
  22. package/core/engines/cocos/injects/3x/init.js +19 -438
  23. package/core/engines/cocos/injects/3x/main.js +2 -52
  24. package/core/engines/cocos/injects/libs/pako.js +1 -2
  25. package/core/engines/cocos/mcp-refresh.js +2 -76
  26. package/core/engines/cocos/merge.js +3 -148
  27. package/core/engines/cocos/pack.js +1 -187
  28. package/core/engines/cocos/preview-cache.js +2 -48
  29. package/core/engines/cocos/preview-cache.test.js +1 -32
  30. package/core/engines/cocos/preview-concat.js +5 -189
  31. package/core/engines/cocos/preview-concat.test.js +1 -73
  32. package/core/engines/cocos/preview-json-hook.js +1 -200
  33. package/core/engines/cocos/preview-json-pack.js +3 -376
  34. package/core/engines/cocos/preview-json-pack.test.js +2 -226
  35. package/core/engines/cocos/preview-proxy.js +1 -132
  36. package/core/engines/cocos/rebundle-preview.js +2 -54
  37. package/core/engines/cocos/strip-social-share.js +3 -115
  38. package/core/engines/cocos/theme-assets.js +2 -161
  39. package/core/engines/cocos/writable-cli.js +2 -248
  40. package/core/engines/cocos/writable-cli.test.js +1 -112
  41. package/core/engines/web/index.js +1 -20
  42. package/core/engines/web/vite.dev.js +17 -322
  43. package/core/engines/web/webpack.build.js +37 -522
  44. package/core/engines/web/webpack.common.js +11 -177
  45. package/core/engines/web/webpack.dev.js +6 -88
  46. package/core/index.js +7 -101
  47. package/core/injectors/apply.js +1 -94
  48. package/core/injectors/check-channel-matrix.js +2 -130
  49. package/core/injectors/spec.js +9 -217
  50. package/core/loaders/gltf-loader.js +28 -74
  51. package/core/options.js +16 -353
  52. package/core/plugins/AdikteevInjectorPlugin.js +4 -26
  53. package/core/plugins/BigoAdsInjectorPlugin.js +4 -21
  54. package/core/plugins/DAPIInjectorPlugin.js +2 -25
  55. package/core/plugins/DebuggerInjectionPlugin.js +3 -31
  56. package/core/plugins/ExitAPIInjectorPlugin.js +3 -56
  57. package/core/plugins/FflateCompressionPlugin.js +40 -231
  58. package/core/plugins/FontInjectionWebpackPlugin.js +4 -46
  59. package/core/plugins/LiftoffInjectorPlugin.js +4 -26
  60. package/core/plugins/MRAIDInjectorPlugin.js +2 -24
  61. package/core/plugins/MintegralInjectorPlugin.js +2 -25
  62. package/core/plugins/PangleInjectorPlugin.js +2 -24
  63. package/core/plugins/PlaycraftOrientationInjectorPlugin.js +3 -29
  64. package/core/plugins/SnapchatInjectorPlugin.js +4 -26
  65. package/core/plugins/SocialMediaInjectorPlugin.js +7 -119
  66. package/core/plugins/TikTokInjectorPlugin.js +2 -24
  67. package/core/plugins/UnityInjectorPlugin.js +8 -37
  68. package/core/plugins/ZipPlugin.js +20 -154
  69. package/core/plugins/socialMediaVitePlugin.js +2 -108
  70. package/core/utils/buildDefines.js +2 -30
  71. package/core/utils/buildTemplateString.js +5 -39
  72. package/core/utils/date.js +1 -16
  73. package/core/utils/fontRegistry.js +13 -81
  74. package/core/utils/fontResolver.js +37 -335
  75. package/core/utils/generateAdikteevHtmlWebpackPluginConfig.js +7 -42
  76. package/core/utils/generateBigabidHtmlWebpackPluginConfig.js +3 -30
  77. package/core/utils/generateInMobiHtmlWebpackPluginConfig.js +7 -43
  78. package/core/utils/getPlaycraftOrientationSuffix.js +2 -9
  79. package/core/utils/injectSDKPlugins.js +13 -68
  80. package/core/utils/logOptions.js +5 -46
  81. package/core/utils/mergeOptions.js +2 -21
  82. package/core/utils/obfuscateLevels.js +10 -0
  83. package/core/utils/obfuscateLevels.test.js +10 -0
  84. package/core/utils/parseArgvOptions.js +5 -125
  85. package/core/utils/resolveChannelFold2Zip.js +3 -15
  86. package/core/utils/validateThemeData.js +26 -227
  87. package/core/validators/pre-build-checker.js +25 -287
  88. package/core/validators/tracking-validator.js +40 -441
  89. package/core/validators/tracking-validator.test.js +2 -84
  90. package/core/vite.dev.js +1 -1
  91. package/core/webpack.build.js +1 -1
  92. package/core/webpack.common.js +1 -1
  93. package/core/webpack.dev.js +1 -1
  94. package/package.json +2 -3
  95. package/README.md +0 -262
@@ -1,177 +1,11 @@
1
- const path = require('path');
2
- const fs = require('fs');
3
- const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
4
- const { VueLoaderPlugin } = require('vue-loader');
5
- const { options } = require('../../options');
6
-
7
- const tsConfigPath = path.resolve(options.tsConfig);
8
- const jsConfigPath = path.resolve(options.jsConfig);
9
- let jsTsConfigPath = null;
10
- if (fs.existsSync(tsConfigPath)) {
11
- jsTsConfigPath = tsConfigPath;
12
- } else if (fs.existsSync(jsConfigPath)) {
13
- jsTsConfigPath = jsConfigPath;
14
- }
15
-
16
- /** @type {string[]} List of file extensions that webpack will resolve */
17
- const allowedExtensions = [
18
- '.vue',
19
- '.ts',
20
- '.tsx',
21
- '.js',
22
- '.json',
23
- '.json5',
24
- '.png',
25
- '.glb',
26
- '.fbx',
27
- '.obj',
28
- '.jpg',
29
- '.webp',
30
- '.mp3',
31
- '.svg',
32
- '.xml',
33
- '.atlas',
34
- '.fnt',
35
- '.css',
36
- '.gif',
37
- '.mp4',
38
- '.woff',
39
- '.woff2',
40
- '.ttf',
41
- '.otf'
42
- ];
43
-
44
- /** @type {import('webpack').Configuration} Base webpack configuration used by both development and build configs */
45
- const webpackConfig = {
46
- entry: path.resolve('src/index'),
47
- resolve: {
48
- extensions: allowedExtensions,
49
- alias: {
50
- assets: path.resolve('assets'),
51
- vue: 'vue/dist/vue.esm-bundler.js',
52
- '@': path.resolve('src'),
53
- },
54
- plugins: jsTsConfigPath
55
- ? [
56
- new TsconfigPathsPlugin({
57
- configFile: jsTsConfigPath,
58
- extensions: allowedExtensions
59
- })
60
- ]
61
- : []
62
- },
63
- output: {
64
- filename: 'build.js',
65
- path: path.resolve('dist')
66
- },
67
- module: {
68
- rules: [
69
- // Vue 单文件组件
70
- {
71
- test: /\.vue$/,
72
- loader: 'vue-loader'
73
- },
74
- // {
75
- // test: /\.tsx?$/,
76
- // use: 'ts-loader',
77
- // exclude: /node_modules/,
78
- // },
79
- {
80
- test: /\.ts?$/,
81
- exclude: /node_modules/,
82
- use: [
83
- {
84
- loader: 'babel-loader',
85
- options: {
86
- // presets: ['@babel/preset-env'],
87
- plugins:
88
- options.compilation.allowTemplateLiterals === false ? ['@babel/plugin-transform-template-literals'] : []
89
- }
90
- },
91
-
92
- {
93
- loader: 'esbuild-loader',
94
- options: {
95
- loader: 'ts',
96
- target: 'es2015'
97
- }
98
- }
99
- ]
100
- },
101
- {
102
- test: /\.css$/,
103
- use: ['style-loader', 'css-loader']
104
- },
105
- {
106
- test: /\.(js|mjs)$/,
107
- // exclude: /node_modules/,
108
- use: {
109
- loader: 'babel-loader',
110
- options: {
111
- // presets: ['@babel/preset-env'],
112
- plugins: options.compilation.allowTemplateLiterals === false ? ['@babel/plugin-transform-template-literals'] : []
113
- }
114
- }
115
- },
116
- {
117
- test: /\.(gltf)$/,
118
- loader: path.join(__dirname, '../../loaders/gltf-loader.js')
119
- },
120
- {
121
- test: /\.(gif|png|jpe?g|webp|mp3|mpeg|mp4|m4a|ogg|wav|glb|fbx|obj)$/i,
122
- type: 'asset/inline'
123
- },
124
- {
125
- test: /\.json5$/i,
126
- type: 'javascript/auto',
127
- use: {
128
- loader: 'json5-loader',
129
- options: {
130
- esModule: false
131
- }
132
- }
133
- },
134
- // 处理 json?url → base64
135
- {
136
- test: /\.json$/i,
137
- resourceQuery: /url/,
138
- type: 'asset/inline',
139
- generator: {
140
- dataUrl: (content) => `data:application/json;base64,${content.toString('base64')}`
141
- }
142
- },
143
- {
144
- test: /\.(svg|xml)$/i,
145
- type: 'asset/source'
146
- },
147
- {
148
- test: /\.atlas$/,
149
- type: 'asset/inline',
150
- generator: {
151
- dataUrl: (content) => {
152
- return `data:text/atlas;base64,${content.toString('base64')}`;
153
- }
154
- }
155
- },
156
- {
157
- test: /\.fnt$/i,
158
- type: 'asset/inline',
159
- generator: {
160
- dataUrl: (content) => {
161
- return `data:text/plain;base64,${content.toString('base64')}`;
162
- }
163
- }
164
- },
165
- {
166
- test: /\.(woff|woff2|eot|ttf|otf)$/i,
167
- type: 'asset/inline'
168
- }
169
- ]
170
- },
171
- plugins: [
172
- new VueLoaderPlugin()
173
- ]
174
- };
175
-
176
- exports.allowedExtensions = allowedExtensions;
177
- exports.webpackCommonConfig = webpackConfig;
1
+ "use strict";const path=require("path");const fs=require("fs");const TsconfigPathsPlugin=require("tsconfig-paths-webpack-plugin");const{VueLoaderPlugin}=require("vue-loader");const{options}=require("../../options");const tsConfigPath=path.resolve(options.tsConfig);const jsConfigPath=path.resolve(options.jsConfig);let jsTsConfigPath=null;if(fs.existsSync(tsConfigPath)){jsTsConfigPath=tsConfigPath}else if(fs.existsSync(jsConfigPath)){jsTsConfigPath=jsConfigPath}/** @type {string[]} List of file extensions that webpack will resolve */const allowedExtensions=[".vue",".ts",".tsx",".js",".json",".json5",".png",".glb",".fbx",".obj",".jpg",".webp",".mp3",".svg",".xml",".atlas",".fnt",".css",".gif",".mp4",".woff",".woff2",".ttf",".otf"];/** @type {import('webpack').Configuration} Base webpack configuration used by both development and build configs */const webpackConfig={entry:path.resolve("src/index"),resolve:{extensions:allowedExtensions,alias:{assets:path.resolve("assets"),vue:"vue/dist/vue.esm-bundler.js","@":path.resolve("src")},plugins:jsTsConfigPath?[new TsconfigPathsPlugin({configFile:jsTsConfigPath,extensions:allowedExtensions})]:[]},output:{filename:"build.js",path:path.resolve("dist")},module:{rules:[// Vue 单文件组件
2
+ {test:/\.vue$/,loader:"vue-loader"},// {
3
+ // test: /\.tsx?$/,
4
+ // use: 'ts-loader',
5
+ // exclude: /node_modules/,
6
+ // },
7
+ {test:/\.ts?$/,exclude:/node_modules/,use:[{loader:"babel-loader",options:{// presets: ['@babel/preset-env'],
8
+ plugins:options.compilation.allowTemplateLiterals===false?["@babel/plugin-transform-template-literals"]:[]}},{loader:"esbuild-loader",options:{loader:"ts",target:"es2015"}}]},{test:/\.css$/,use:["style-loader","css-loader"]},{test:/\.(js|mjs)$/,// exclude: /node_modules/,
9
+ use:{loader:"babel-loader",options:{// presets: ['@babel/preset-env'],
10
+ plugins:options.compilation.allowTemplateLiterals===false?["@babel/plugin-transform-template-literals"]:[]}}},{test:/\.(gltf)$/,loader:path.join(__dirname,"../../loaders/gltf-loader.js")},{test:/\.(gif|png|jpe?g|webp|mp3|mpeg|mp4|m4a|ogg|wav|glb|fbx|obj)$/i,type:"asset/inline"},{test:/\.json5$/i,type:"javascript/auto",use:{loader:"json5-loader",options:{esModule:false}}},// 处理 json?url → base64
11
+ {test:/\.json$/i,resourceQuery:/url/,type:"asset/inline",generator:{dataUrl:content=>`data:application/json;base64,${content.toString("base64")}`}},{test:/\.(svg|xml)$/i,type:"asset/source"},{test:/\.atlas$/,type:"asset/inline",generator:{dataUrl:content=>{return`data:text/atlas;base64,${content.toString("base64")}`}}},{test:/\.fnt$/i,type:"asset/inline",generator:{dataUrl:content=>{return`data:text/plain;base64,${content.toString("base64")}`}}},{test:/\.(woff|woff2|eot|ttf|otf)$/i,type:"asset/inline"}]},plugins:[new VueLoaderPlugin]};exports.allowedExtensions=allowedExtensions;exports.webpackCommonConfig=webpackConfig;
@@ -1,98 +1,16 @@
1
- const { merge } = require('webpack-merge');
2
- const webpack = require('webpack');
3
- const HtmlWebpackPlugin = require('html-webpack-plugin');
4
- const WebpackDevServer = require('webpack-dev-server');
5
- const { webpackCommonConfig } = require('./webpack.common.js');
6
- const path = require('path');
7
- const { DebuggerInjectionPlugin } = require('../../plugins/DebuggerInjectionPlugin.js');
8
- const { options } = require('../../options.js');
9
- const { mergeOptions } = require('../../utils/mergeOptions.js');
10
- const { buildDefines } = require('../../utils/buildDefines.js');
11
- const { logOptions } = require('../../utils/logOptions.js');
12
- const { injectSDKPlugins } = require('../../utils/injectSDKPlugins.js');
13
- const { PlaycraftOrientationInjectorPlugin } = require('../../plugins/PlaycraftOrientationInjectorPlugin.js');
14
-
15
- /**
1
+ "use strict";const{merge}=require("webpack-merge");const webpack=require("webpack");const HtmlWebpackPlugin=require("html-webpack-plugin");const WebpackDevServer=require("webpack-dev-server");const{webpackCommonConfig}=require("./webpack.common.js");const path=require("path");const{DebuggerInjectionPlugin}=require("../../plugins/DebuggerInjectionPlugin.js");const{options}=require("../../options.js");const{mergeOptions}=require("../../utils/mergeOptions.js");const{buildDefines}=require("../../utils/buildDefines.js");const{logOptions}=require("../../utils/logOptions.js");const{injectSDKPlugins}=require("../../utils/injectSDKPlugins.js");const{PlaycraftOrientationInjectorPlugin}=require("../../plugins/PlaycraftOrientationInjectorPlugin.js");/**
16
2
  * Creates webpack configuration for development
17
3
  * @param {Partial<import('./index').CLIOptions>} [customOptions] - Custom options to merge with default options
18
4
  * @param {Record<string, any>} [customDefines] - Additional defines for webpack.DefinePlugin
19
5
  * @param {import('webpack').Configuration} [webpackCustomConfig] - Custom webpack config to merge
20
6
  * @returns {import('webpack').Configuration} Final webpack development configuration
21
- */
22
- function makeWebpackDevConfig(customOptions, customDefines, webpackCustomConfig) {
23
- const devOptions = mergeOptions(options, customOptions);
24
- logOptions(devOptions)
25
- customDefines = customDefines || {};
26
- webpackCustomConfig = webpackCustomConfig || {};
27
-
28
- /** @type {'both'|'landscape'|'portrait'} */
29
- const playcraftOrientationValue = devOptions.playcraftOrientationValue || 'both';
30
-
31
- const webpackConfig = merge(
32
- webpackCommonConfig,
33
- {
34
- mode: 'development',
35
- devtool: 'inline-source-map',
36
- devServer: {
37
- static: {
38
- directory: path.resolve('dist')
39
- },
40
- hot: true,
41
- // compress: true,
42
- port: devOptions['port'],
43
- allowedHosts: 'all',
44
- open: devOptions['open']
45
- },
46
- plugins: [
47
- new HtmlWebpackPlugin({
48
- template: path.resolve('src/index.html'),
49
- inlineSource: '.(js|css|png|jpg|webp|svg|xml|atlas|mp3|gif|glb|fbx|obj)$',
50
- meta: {
51
- viewport: 'width=device-width,initial-scale=1.0,viewport-fit=cover,maximum-scale=1.0,user-scalable=no'
52
- }
53
- }),
54
- // 注入 window.playcraftOrientation 全局变量,标识当前构建产物对应的横竖屏方向版本
55
- new PlaycraftOrientationInjectorPlugin(playcraftOrientationValue),
56
-
57
- new webpack.DefinePlugin({
58
- ...buildDefines(devOptions),
59
- ...devOptions.defines,
60
- __DEV__: JSON.stringify(devOptions['dev'] === undefined ? true : devOptions['dev']),
61
- ...customDefines
62
- })
63
- ]
64
- },
65
- webpackCustomConfig
66
- );
67
-
68
- if (devOptions['debugger']) {
69
- webpackConfig.plugins.push(new DebuggerInjectionPlugin(devOptions['debugger']));
70
- }
71
-
72
- // 注入渠道 SDK 插件(与 build 保持一致)
73
- injectSDKPlugins(webpackConfig, {
74
- network: devOptions['network'],
75
- protocol: devOptions['protocol'],
76
- playcraftOrientationValue
77
- });
78
-
79
- return webpackConfig;
80
- }
81
-
82
- /**
7
+ */function makeWebpackDevConfig(customOptions,customDefines,webpackCustomConfig){const devOptions=mergeOptions(options,customOptions);logOptions(devOptions);customDefines=customDefines||{};webpackCustomConfig=webpackCustomConfig||{};/** @type {'both'|'landscape'|'portrait'} */const playcraftOrientationValue=devOptions.playcraftOrientationValue||"both";const webpackConfig=merge(webpackCommonConfig,{mode:"development",devtool:"inline-source-map",devServer:{static:{directory:path.resolve("dist")},hot:true,// compress: true,
8
+ port:devOptions["port"],allowedHosts:"all",open:devOptions["open"]},plugins:[new HtmlWebpackPlugin({template:path.resolve("src/index.html"),inlineSource:".(js|css|png|jpg|webp|svg|xml|atlas|mp3|gif|glb|fbx|obj)$",meta:{viewport:"width=device-width,initial-scale=1.0,viewport-fit=cover,maximum-scale=1.0,user-scalable=no"}}),// 注入 window.playcraftOrientation 全局变量,标识当前构建产物对应的横竖屏方向版本
9
+ new PlaycraftOrientationInjectorPlugin(playcraftOrientationValue),new webpack.DefinePlugin({...buildDefines(devOptions),...devOptions.defines,__DEV__:JSON.stringify(devOptions["dev"]===undefined?true:devOptions["dev"]),...customDefines})]},webpackCustomConfig);if(devOptions["debugger"]){webpackConfig.plugins.push(new DebuggerInjectionPlugin(devOptions["debugger"]))}// 注入渠道 SDK 插件(与 build 保持一致)
10
+ injectSDKPlugins(webpackConfig,{network:devOptions["network"],protocol:devOptions["protocol"],playcraftOrientationValue});return webpackConfig}/**
83
11
  * Starts webpack development server
84
12
  * @param {import('webpack').Configuration} [webpackConfig] - Webpack configuration to use, creates default if not provided
85
13
  * @param {Partial<import('./index').CLIOptions>} [customOptions] - Custom options to merge with default options
86
14
  * @param {Record<string, any>} [customDefines] - Additional defines for webpack.DefinePlugin
87
15
  * @param {import('webpack').Configuration} [webpackCustomConfig] - Custom webpack config to merge
88
- */
89
- function runDev(webpackConfig, customOptions, customDefines, webpackCustomConfig) {
90
- if (!webpackConfig) webpackConfig = makeWebpackDevConfig(customOptions, customDefines, webpackCustomConfig);
91
-
92
- const compiler = webpack(webpackConfig);
93
- const server = new WebpackDevServer(webpackConfig.devServer, compiler);
94
- server.start();
95
- }
96
-
97
- exports.makeWebpackDevConfig = makeWebpackDevConfig;
98
- exports.runDev = runDev;
16
+ */function runDev(webpackConfig,customOptions,customDefines,webpackCustomConfig){if(!webpackConfig)webpackConfig=makeWebpackDevConfig(customOptions,customDefines,webpackCustomConfig);const compiler=webpack(webpackConfig);const server=new WebpackDevServer(webpackConfig.devServer,compiler);server.start()}exports.makeWebpackDevConfig=makeWebpackDevConfig;exports.runDev=runDev;
package/core/index.js CHANGED
@@ -1,101 +1,7 @@
1
- const { allowedExtensions, webpackCommonConfig } = require('./webpack.common.js');
2
- const { parseArgvOptions } = require('./utils/parseArgvOptions.js');
3
- const { mergeOptions } = require('./utils/mergeOptions.js');
4
- const { options } = require('./options.js');
5
- const { DAPIInjectorPlugin } = require('./plugins/DAPIInjectorPlugin.js');
6
- const { DebuggerInjectionPlugin } = require('./plugins/DebuggerInjectionPlugin.js');
7
- const { ExitAPIInjectorPlugin } = require('./plugins/ExitAPIInjectorPlugin.js');
8
- const { runBuild, makeWebpackBuildConfig } = require('./webpack.build.js');
9
- const { runDev, makeWebpackDevConfig } = require('./webpack.dev.js');
10
- const { runViteDev, makeViteDevConfig, json5Plugin, assetInlinePlugin, fontFamilyPlugin } = require('./vite.dev.js');
11
-
12
- // 字体注入
13
- const { FontInjectionWebpackPlugin } = require('./plugins/FontInjectionWebpackPlugin.js');
14
- const { FONT_REGISTRY, getRegisteredFontKeys, isBuiltin: isBuiltinFont, getFontEntry } = require('./utils/fontRegistry.js');
15
- const { getOrGenerateFontCSS } = require('./utils/fontResolver.js');
16
-
17
- // 验证器
18
- const {
19
- runPreBuildChecks,
20
- runTrackingCheck,
21
- registerChecker,
22
- toggleChecker,
23
- listCheckers
24
- } = require('./validators/pre-build-checker.js');
25
-
26
- const {
27
- validateTracking,
28
- printValidationResult,
29
- TRACKING_METHODS,
30
- SDK_TO_TRACKING_MAP
31
- } = require('./validators/tracking-validator.js');
32
-
33
- exports.mergeOptions = mergeOptions;
34
- exports.parseArgvOptions = parseArgvOptions;
35
-
36
- exports.options = options;
37
-
38
- exports.allowedExtensions = allowedExtensions;
39
- exports.webpackCommonConfig = webpackCommonConfig;
40
- exports.makeWebpackDevConfig = makeWebpackDevConfig;
41
- exports.makeWebpackBuildConfig = makeWebpackBuildConfig;
42
- exports.runDev = runDev;
43
- exports.runBuild = runBuild;
44
-
45
- exports.DAPIInjectorPlugin = DAPIInjectorPlugin;
46
- exports.ExitAPIInjectorPlugin = ExitAPIInjectorPlugin;
47
- exports.DebuggerInjectionPlugin = DebuggerInjectionPlugin;
48
-
49
- exports.makeViteDevConfig = makeViteDevConfig;
50
- exports.runViteDev = runViteDev;
51
-
52
- // 公共 Vite 插件(供项目 vite.config.ts 直接引用)
53
- exports.json5Plugin = json5Plugin;
54
- exports.assetInlinePlugin = assetInlinePlugin;
55
- exports.fontFamilyPlugin = fontFamilyPlugin;
56
-
57
- // 编译前检查器导出
58
- exports.runPreBuildChecks = runPreBuildChecks;
59
- exports.runTrackingCheck = runTrackingCheck;
60
- exports.registerChecker = registerChecker;
61
- exports.toggleChecker = toggleChecker;
62
- exports.listCheckers = listCheckers;
63
-
64
- // 埋点验证器导出
65
- exports.validateTracking = validateTracking;
66
- exports.printValidationResult = printValidationResult;
67
- exports.TRACKING_METHODS = TRACKING_METHODS;
68
- exports.SDK_TO_TRACKING_MAP = SDK_TO_TRACKING_MAP;
69
-
70
- // 字体注入导出
71
- exports.FontInjectionWebpackPlugin = FontInjectionWebpackPlugin;
72
- exports.FONT_REGISTRY = FONT_REGISTRY;
73
- exports.getRegisteredFontKeys = getRegisteredFontKeys;
74
- exports.isBuiltinFont = isBuiltinFont;
75
- exports.getFontEntry = getFontEntry;
76
- exports.getOrGenerateFontCSS = getOrGenerateFontCSS;
77
-
78
- const { runCocosBuild, syncThemeForPreview } = require('./engines/cocos/pack.js');
79
- const { runCocosEngineBuild } = require('./engines/cocos/cli-build.js');
80
- const { prepareWritableCocosCli } = require('./engines/cocos/writable-cli.js');
81
- const { syncGeneratedTheme } = require('./engines/cocos/theme-assets.js');
82
- const { refreshAssets } = require('./engines/cocos/mcp-refresh.js');
83
- const { spawnCocos, needsXvfb } = require('./engines/cocos/cli.js');
84
- const {
85
- stripAdsdkCjsImport,
86
- stripSocialShareFromDir,
87
- isSocialMediaNetwork,
88
- } = require('./engines/cocos/strip-social-share.js');
89
- const { runCocosDev } = require('./engines/cocos/dev-server.js');
90
- exports.runCocosBuild = runCocosBuild;
91
- exports.syncThemeForPreview = syncThemeForPreview;
92
- exports.runCocosEngineBuild = runCocosEngineBuild;
93
- exports.prepareWritableCocosCli = prepareWritableCocosCli;
94
- exports.syncGeneratedTheme = syncGeneratedTheme;
95
- exports.refreshAssets = refreshAssets;
96
- exports.spawnCocos = spawnCocos;
97
- exports.needsXvfb = needsXvfb;
98
- exports.stripAdsdkCjsImport = stripAdsdkCjsImport;
99
- exports.stripSocialShareFromDir = stripSocialShareFromDir;
100
- exports.isSocialMediaNetwork = isSocialMediaNetwork;
101
- exports.runCocosDev = runCocosDev;
1
+ "use strict";const{allowedExtensions,webpackCommonConfig}=require("./webpack.common.js");const{parseArgvOptions}=require("./utils/parseArgvOptions.js");const{mergeOptions}=require("./utils/mergeOptions.js");const{options}=require("./options.js");const{DAPIInjectorPlugin}=require("./plugins/DAPIInjectorPlugin.js");const{DebuggerInjectionPlugin}=require("./plugins/DebuggerInjectionPlugin.js");const{ExitAPIInjectorPlugin}=require("./plugins/ExitAPIInjectorPlugin.js");const{runBuild,makeWebpackBuildConfig}=require("./webpack.build.js");const{runDev,makeWebpackDevConfig}=require("./webpack.dev.js");const{runViteDev,makeViteDevConfig,json5Plugin,assetInlinePlugin,fontFamilyPlugin}=require("./vite.dev.js");// 字体注入
2
+ const{FontInjectionWebpackPlugin}=require("./plugins/FontInjectionWebpackPlugin.js");const{FONT_REGISTRY,getRegisteredFontKeys,isBuiltin:isBuiltinFont,getFontEntry}=require("./utils/fontRegistry.js");const{getOrGenerateFontCSS}=require("./utils/fontResolver.js");// 验证器
3
+ const{runPreBuildChecks,runTrackingCheck,registerChecker,toggleChecker,listCheckers}=require("./validators/pre-build-checker.js");const{validateTracking,printValidationResult,TRACKING_METHODS,SDK_TO_TRACKING_MAP}=require("./validators/tracking-validator.js");exports.mergeOptions=mergeOptions;exports.parseArgvOptions=parseArgvOptions;exports.options=options;exports.allowedExtensions=allowedExtensions;exports.webpackCommonConfig=webpackCommonConfig;exports.makeWebpackDevConfig=makeWebpackDevConfig;exports.makeWebpackBuildConfig=makeWebpackBuildConfig;exports.runDev=runDev;exports.runBuild=runBuild;exports.DAPIInjectorPlugin=DAPIInjectorPlugin;exports.ExitAPIInjectorPlugin=ExitAPIInjectorPlugin;exports.DebuggerInjectionPlugin=DebuggerInjectionPlugin;exports.makeViteDevConfig=makeViteDevConfig;exports.runViteDev=runViteDev;// 公共 Vite 插件(供项目 vite.config.ts 直接引用)
4
+ exports.json5Plugin=json5Plugin;exports.assetInlinePlugin=assetInlinePlugin;exports.fontFamilyPlugin=fontFamilyPlugin;// 编译前检查器导出
5
+ exports.runPreBuildChecks=runPreBuildChecks;exports.runTrackingCheck=runTrackingCheck;exports.registerChecker=registerChecker;exports.toggleChecker=toggleChecker;exports.listCheckers=listCheckers;// 埋点验证器导出
6
+ exports.validateTracking=validateTracking;exports.printValidationResult=printValidationResult;exports.TRACKING_METHODS=TRACKING_METHODS;exports.SDK_TO_TRACKING_MAP=SDK_TO_TRACKING_MAP;// 字体注入导出
7
+ exports.FontInjectionWebpackPlugin=FontInjectionWebpackPlugin;exports.FONT_REGISTRY=FONT_REGISTRY;exports.getRegisteredFontKeys=getRegisteredFontKeys;exports.isBuiltinFont=isBuiltinFont;exports.getFontEntry=getFontEntry;exports.getOrGenerateFontCSS=getOrGenerateFontCSS;const{runCocosBuild,syncThemeForPreview}=require("./engines/cocos/pack.js");const{runCocosEngineBuild}=require("./engines/cocos/cli-build.js");const{prepareWritableCocosCli}=require("./engines/cocos/writable-cli.js");const{syncGeneratedTheme}=require("./engines/cocos/theme-assets.js");const{refreshAssets}=require("./engines/cocos/mcp-refresh.js");const{spawnCocos,needsXvfb}=require("./engines/cocos/cli.js");const{stripAdsdkCjsImport,stripSocialShareFromDir,isSocialMediaNetwork}=require("./engines/cocos/strip-social-share.js");const{runCocosDev}=require("./engines/cocos/dev-server.js");exports.runCocosBuild=runCocosBuild;exports.syncThemeForPreview=syncThemeForPreview;exports.runCocosEngineBuild=runCocosEngineBuild;exports.prepareWritableCocosCli=prepareWritableCocosCli;exports.syncGeneratedTheme=syncGeneratedTheme;exports.refreshAssets=refreshAssets;exports.spawnCocos=spawnCocos;exports.needsXvfb=needsXvfb;exports.stripAdsdkCjsImport=stripAdsdkCjsImport;exports.stripSocialShareFromDir=stripSocialShareFromDir;exports.isSocialMediaNetwork=isSocialMediaNetwork;exports.runCocosDev=runCocosDev;
@@ -1,94 +1 @@
1
- 'use strict';
2
-
3
- const { load } = require('cheerio');
4
- const { META, LIFTOFF_FONT } = require('./spec');
5
-
6
- function renderTag(tag) {
7
- const attrs = tag.attributes || {};
8
- const attrStr = Object.entries(attrs)
9
- .map(([k, v]) => `${k}="${String(v).replace(/"/g, '&quot;')}"`)
10
- .join(' ');
11
- if (tag.tagName === 'meta') {
12
- return `<meta ${attrStr} />`;
13
- }
14
- const inner = tag.innerHTML || '';
15
- return `<script${attrStr ? ' ' + attrStr : ''}>${inner}</script>`;
16
- }
17
-
18
- function applyMeta($, spec) {
19
- const viewport = spec.viewport || META.COMMON_VIEWPORT;
20
- if ($('meta[charset]').length) {
21
- $('meta[charset]').first().attr('charset', META.COMMON_CHARSET);
22
- } else {
23
- $('head').prepend(`<meta charset="${META.COMMON_CHARSET}" />`);
24
- }
25
- if ($('meta[name="viewport"]').length) {
26
- $('meta[name="viewport"]').attr('content', viewport);
27
- } else {
28
- $('head').prepend(`<meta name="viewport" content="${viewport}" />`);
29
- }
30
- if (!spec.skipRecommendedMeta) {
31
- if (!$('meta[name="HandheldFriendly"]').length) {
32
- $('head').append('<meta name="HandheldFriendly" content="True" />');
33
- }
34
- if (!$('meta[http-equiv="cleartype"]').length) {
35
- $('head').append('<meta http-equiv="cleartype" content="on" />');
36
- }
37
- if (!$('meta[name="apple-mobile-web-app-capable"]').length) {
38
- $('head').append('<meta name="apple-mobile-web-app-capable" content="yes" />');
39
- }
40
- if (!$('meta[name="mobile-web-app-capable"]').length) {
41
- $('head').append('<meta name="mobile-web-app-capable" content="yes" />');
42
- }
43
- if (!$('meta[http-equiv="X-UA-Compatible"]').length) {
44
- $('head').append('<meta http-equiv="X-UA-Compatible" content="IE=10" />');
45
- }
46
- }
47
- if (spec.liftoffFont) {
48
- $('head').append(LIFTOFF_FONT);
49
- }
50
- }
51
-
52
- function applyInjections(html, spec) {
53
- const $ = load(html);
54
- applyMeta($, spec);
55
- const tags = (spec.headTags || []).slice().reverse();
56
- for (const tag of tags) {
57
- $('head').prepend(renderTag(tag));
58
- }
59
- let out = $.html();
60
- for (const transform of spec.htmlTransforms || []) {
61
- out = transform(out);
62
- }
63
- if (spec.scanStubHtml) {
64
- if (/<\/body>/i.test(out)) {
65
- out = out.replace(/<\/body>/i, `${spec.scanStubHtml}</body>`);
66
- } else {
67
- out += spec.scanStubHtml;
68
- }
69
- }
70
- return out;
71
- }
72
-
73
- function extractInlineScripts(html) {
74
- const $ = load(html);
75
- const scripts = [];
76
- $('script').each((_i, el) => {
77
- const $el = $(el);
78
- if ($el.attr('src')) return;
79
- scripts.push($el.html() || '');
80
- $el.remove();
81
- });
82
- return { html: $.html(), scripts };
83
- }
84
-
85
- function extractBodyScripts(html) {
86
- return extractInlineScripts(html);
87
- }
88
-
89
- module.exports = {
90
- applyInjections,
91
- extractBodyScripts,
92
- extractInlineScripts,
93
- renderTag,
94
- };
1
+ "use strict";const{load}=require("cheerio");const{META,LIFTOFF_FONT}=require("./spec");function renderTag(tag){const attrs=tag.attributes||{};const attrStr=Object.entries(attrs).map(([k,v])=>`${k}="${String(v).replace(/"/g,"&quot;")}"`).join(" ");if(tag.tagName==="meta"){return`<meta ${attrStr} />`}const inner=tag.innerHTML||"";return`<script${attrStr?" "+attrStr:""}>${inner}</script>`}function applyMeta($,spec){const viewport=spec.viewport||META.COMMON_VIEWPORT;if($("meta[charset]").length){$("meta[charset]").first().attr("charset",META.COMMON_CHARSET)}else{$("head").prepend(`<meta charset="${META.COMMON_CHARSET}" />`)}if($("meta[name=\"viewport\"]").length){$("meta[name=\"viewport\"]").attr("content",viewport)}else{$("head").prepend(`<meta name="viewport" content="${viewport}" />`)}if(!spec.skipRecommendedMeta){if(!$("meta[name=\"HandheldFriendly\"]").length){$("head").append("<meta name=\"HandheldFriendly\" content=\"True\" />")}if(!$("meta[http-equiv=\"cleartype\"]").length){$("head").append("<meta http-equiv=\"cleartype\" content=\"on\" />")}if(!$("meta[name=\"apple-mobile-web-app-capable\"]").length){$("head").append("<meta name=\"apple-mobile-web-app-capable\" content=\"yes\" />")}if(!$("meta[name=\"mobile-web-app-capable\"]").length){$("head").append("<meta name=\"mobile-web-app-capable\" content=\"yes\" />")}if(!$("meta[http-equiv=\"X-UA-Compatible\"]").length){$("head").append("<meta http-equiv=\"X-UA-Compatible\" content=\"IE=10\" />")}}if(spec.liftoffFont){$("head").append(LIFTOFF_FONT)}}function applyInjections(html,spec){const $=load(html);applyMeta($,spec);const tags=(spec.headTags||[]).slice().reverse();for(const tag of tags){$("head").prepend(renderTag(tag))}let out=$.html();for(const transform of spec.htmlTransforms||[]){out=transform(out)}if(spec.scanStubHtml){if(/<\/body>/i.test(out)){out=out.replace(/<\/body>/i,`${spec.scanStubHtml}</body>`)}else{out+=spec.scanStubHtml}}return out}function extractInlineScripts(html){const $=load(html);const scripts=[];$("script").each((_i,el)=>{const $el=$(el);if($el.attr("src"))return;scripts.push($el.html()||"");$el.remove()});return{html:$.html(),scripts}}function extractBodyScripts(html){return extractInlineScripts(html)}module.exports={applyInjections,extractBodyScripts,extractInlineScripts,renderTag};
@@ -1,137 +1,9 @@
1
1
  #!/usr/bin/env node
2
- 'use strict';
3
-
4
- /**
2
+ "use strict";/**
5
3
  * Compare Cocos pack plan + injections against webpack.build.js rules.
6
4
  * Run: node core/injectors/check-channel-matrix.js
7
5
  *
8
6
  * Webpack is not modified. Known intentional Cocos diffs:
9
7
  * facebook / mintegral zip = inlined index.html only
10
8
  * (webpack still skips HtmlInlineScriptPlugin for those two)
11
- */
12
-
13
- const { allowedAdNetworks, mraidPartners } = require('../utils/parseArgvOptions');
14
- const { applyInjections } = require('./apply');
15
- const { getChannelPackPlan, zipOutputNetworks, zipOutputAllowedNetworks } = require('./spec');
16
-
17
- const WEBPACK_ZIP = [
18
- 'google', 'pangle', 'tiktok', 'vungle', 'mytarget', 'mintegral', 'adikteev',
19
- 'bigabid', 'inmobi', 'snapchat', 'bigoads', 'liftoff', 'facebook', 'remerge'
20
- ];
21
- const WEBPACK_ZIP_OPTIONAL = ['moloco'];
22
- const WEBPACK_SKIP_INLINE = ['mintegral', 'adikteev', 'bigabid', 'facebook'];
23
- const INTENTIONAL_INLINE_ZIP = ['facebook', 'mintegral'];
24
-
25
- function webpackProtocol(network) {
26
- return mraidPartners.includes(network) ? 'mraid' : 'none';
27
- }
28
-
29
- function webpackExpect(network) {
30
- const zip = WEBPACK_ZIP.includes(network);
31
- const skipInline = WEBPACK_SKIP_INLINE.includes(network) && (zip || network === 'mintegral' || network === 'adikteev' || network === 'bigabid');
32
- let files;
33
- if (!zip) files = ['*.html'];
34
- else if (network === 'adikteev') files = ['index.html', 'creative.js', 'style.css'];
35
- else if (network === 'bigabid') files = ['index.html', 'main.js'];
36
- else if (network === 'tiktok' || network === 'snapchat' || network === 'bigoads') files = ['index.html', 'config.json'];
37
- else if (skipInline && network === 'facebook') files = ['index.html', 'build.js'];
38
- else if (skipInline && network === 'mintegral') files = ['index.html', 'build.js'];
39
- else files = ['index.html'];
40
- return { zip, skipInline, files };
41
- }
42
-
43
- const INJECT_EXPECT = {
44
- google: ['exitapi.js', 'ad.size', 'ad.orientation'],
45
- tiktok: ['union-fe-nc-i18n'],
46
- pangle: ['union-fe-nc/playable/sdk'],
47
- mintegral: ['mintGameStart'],
48
- unity: ['src="mraid.js"'],
49
- ironsource: ['src="mraid.js"'],
50
- remerge: ['src="mraid.js"'],
51
- liftoff: ['src="mraid.js"', 'Helvetica Neue'],
52
- bigoads: ['bgy-mraid-sdk.js'],
53
- snapchat: ['ScPlayableAd'],
54
- inmobi: ['INMOBI_DSPMACROS'],
55
- social_media: ['og:type'],
56
- };
57
-
58
- const INJECT_FORBID = {
59
- applovin: ['src="mraid.js"'],
60
- facebook: ['src="mraid.js"'],
61
- preview: ['src="mraid.js"'],
62
- google: ['src="mraid.js"'],
63
- };
64
-
65
- const baseHtml = '<html><head></head><body><div id="GameDiv"></div></body></html>';
66
- const failures = [];
67
- const rows = [];
68
-
69
- for (const network of allowedAdNetworks) {
70
- const protocol = webpackProtocol(network);
71
- const buildOptions = {
72
- network,
73
- protocol,
74
- zip: false,
75
- googlePlayUrl: 'https://play.example',
76
- appStoreUrl: 'https://app.example',
77
- playcraftOrientationValue: 'both',
78
- filename: 'Game',
79
- };
80
- const themePayload = { mergedData: { gamePlay: { shareMeta: { title: 'T' } } }, i18nOverride: {}, assetRecordByPath: {} };
81
- const plan = getChannelPackPlan(buildOptions, themePayload);
82
- const wx = webpackExpect(network);
83
- const html = applyInjections(baseHtml, plan.spec);
84
-
85
- const cocosFiles = plan.isZip
86
- ? plan.zipEntries.filter((n, i, a) => a.indexOf(n) === i)
87
- : ['*.html'];
88
-
89
- let expectedCocosFiles = wx.files;
90
- if (INTENTIONAL_INLINE_ZIP.includes(network)) expectedCocosFiles = ['index.html'];
91
-
92
- const zipMismatch = plan.isZip !== wx.zip;
93
- const filesMismatch = plan.isZip && expectedCocosFiles.slice().sort().join() !== cocosFiles.slice().sort().join();
94
-
95
- for (const s of INJECT_EXPECT[network] || []) {
96
- if (!html.includes(s)) failures.push(`${network}: missing inject "${s}"`);
97
- }
98
- for (const s of INJECT_FORBID[network] || []) {
99
- if (html.includes(s)) failures.push(`${network}: unexpected inject "${s}"`);
100
- }
101
- if (zipMismatch) failures.push(`${network}: zip webpack=${wx.zip} cocos=${plan.isZip}`);
102
- if (filesMismatch) failures.push(`${network}: files webpack/cocos expected=${expectedCocosFiles} got=${cocosFiles}`);
103
- if (!html.includes('HandheldFriendly')) failures.push(`${network}: missing recommended meta`);
104
- if (!html.includes('FbPlayableAd.onCTAClick')) failures.push(`${network}: missing channel scan stub`);
105
-
106
- const note = INTENTIONAL_INLINE_ZIP.includes(network) && wx.files.includes('build.js')
107
- ? 'cocos inlines (webpack still splits)'
108
- : '';
109
- rows.push({
110
- network,
111
- zip: plan.isZip ? 'zip' : 'html',
112
- layout: plan.layout,
113
- files: cocosFiles.join(', '),
114
- note,
115
- });
116
- }
117
-
118
- if (JSON.stringify(zipOutputNetworks.slice().sort()) !== JSON.stringify(WEBPACK_ZIP.slice().sort())) {
119
- failures.push(`zipOutputNetworks != webpack list`);
120
- }
121
- if (JSON.stringify(zipOutputAllowedNetworks) !== JSON.stringify(WEBPACK_ZIP_OPTIONAL)) {
122
- failures.push(`zipOutputAllowedNetworks != webpack list`);
123
- }
124
-
125
- console.log('network'.padEnd(14), 'fmt'.padEnd(6), 'layout'.padEnd(12), 'files');
126
- console.log('-'.repeat(80));
127
- for (const row of rows) {
128
- console.log(row.network.padEnd(14), row.zip.padEnd(6), row.layout.padEnd(12), row.files, row.note ? ` ← ${row.note}` : '');
129
- }
130
-
131
- if (failures.length) {
132
- console.error('\nFAIL');
133
- failures.forEach((f) => console.error(' -', f));
134
- process.exit(1);
135
- }
136
- console.log('\nOK all', allowedAdNetworks.length, 'networks');
137
- console.log('Intentional diffs: facebook, mintegral zip contain only index.html (webpack comment still says split JS).');
9
+ */const{allowedAdNetworks,mraidPartners}=require("../utils/parseArgvOptions");const{applyInjections}=require("./apply");const{getChannelPackPlan,zipOutputNetworks,zipOutputAllowedNetworks}=require("./spec");const WEBPACK_ZIP=["google","pangle","tiktok","vungle","mytarget","mintegral","adikteev","bigabid","inmobi","snapchat","bigoads","liftoff","facebook","remerge"];const WEBPACK_ZIP_OPTIONAL=["moloco"];const WEBPACK_SKIP_INLINE=["mintegral","adikteev","bigabid","facebook"];const INTENTIONAL_INLINE_ZIP=["facebook","mintegral"];function webpackProtocol(network){return mraidPartners.includes(network)?"mraid":"none"}function webpackExpect(network){const zip=WEBPACK_ZIP.includes(network);const skipInline=WEBPACK_SKIP_INLINE.includes(network)&&(zip||network==="mintegral"||network==="adikteev"||network==="bigabid");let files;if(!zip)files=["*.html"];else if(network==="adikteev")files=["index.html","creative.js","style.css"];else if(network==="bigabid")files=["index.html","main.js"];else if(network==="tiktok"||network==="snapchat"||network==="bigoads")files=["index.html","config.json"];else if(skipInline&&network==="facebook")files=["index.html","build.js"];else if(skipInline&&network==="mintegral")files=["index.html","build.js"];else files=["index.html"];return{zip,skipInline,files}}const INJECT_EXPECT={google:["exitapi.js","ad.size","ad.orientation"],tiktok:["union-fe-nc-i18n"],pangle:["union-fe-nc/playable/sdk"],mintegral:["mintGameStart"],unity:["src=\"mraid.js\""],ironsource:["src=\"mraid.js\""],remerge:["src=\"mraid.js\""],liftoff:["src=\"mraid.js\"","Helvetica Neue"],bigoads:["bgy-mraid-sdk.js"],snapchat:["ScPlayableAd"],inmobi:["INMOBI_DSPMACROS"],social_media:["og:type"]};const INJECT_FORBID={applovin:["src=\"mraid.js\""],facebook:["src=\"mraid.js\""],preview:["src=\"mraid.js\""],google:["src=\"mraid.js\""]};const baseHtml="<html><head></head><body><div id=\"GameDiv\"></div></body></html>";const failures=[];const rows=[];for(const network of allowedAdNetworks){const protocol=webpackProtocol(network);const buildOptions={network,protocol,zip:false,googlePlayUrl:"https://play.example",appStoreUrl:"https://app.example",playcraftOrientationValue:"both",filename:"Game"};const themePayload={mergedData:{gamePlay:{shareMeta:{title:"T"}}},i18nOverride:{},assetRecordByPath:{}};const plan=getChannelPackPlan(buildOptions,themePayload);const wx=webpackExpect(network);const html=applyInjections(baseHtml,plan.spec);const cocosFiles=plan.isZip?plan.zipEntries.filter((n,i,a)=>a.indexOf(n)===i):["*.html"];let expectedCocosFiles=wx.files;if(INTENTIONAL_INLINE_ZIP.includes(network))expectedCocosFiles=["index.html"];const zipMismatch=plan.isZip!==wx.zip;const filesMismatch=plan.isZip&&expectedCocosFiles.slice().sort().join()!==cocosFiles.slice().sort().join();for(const s of INJECT_EXPECT[network]||[]){if(!html.includes(s))failures.push(`${network}: missing inject "${s}"`)}for(const s of INJECT_FORBID[network]||[]){if(html.includes(s))failures.push(`${network}: unexpected inject "${s}"`)}if(zipMismatch)failures.push(`${network}: zip webpack=${wx.zip} cocos=${plan.isZip}`);if(filesMismatch)failures.push(`${network}: files webpack/cocos expected=${expectedCocosFiles} got=${cocosFiles}`);if(!html.includes("HandheldFriendly"))failures.push(`${network}: missing recommended meta`);if(!html.includes("FbPlayableAd.onCTAClick"))failures.push(`${network}: missing channel scan stub`);const note=INTENTIONAL_INLINE_ZIP.includes(network)&&wx.files.includes("build.js")?"cocos inlines (webpack still splits)":"";rows.push({network,zip:plan.isZip?"zip":"html",layout:plan.layout,files:cocosFiles.join(", "),note})}if(JSON.stringify(zipOutputNetworks.slice().sort())!==JSON.stringify(WEBPACK_ZIP.slice().sort())){failures.push(`zipOutputNetworks != webpack list`)}if(JSON.stringify(zipOutputAllowedNetworks)!==JSON.stringify(WEBPACK_ZIP_OPTIONAL)){failures.push(`zipOutputAllowedNetworks != webpack list`)}console.log("network".padEnd(14),"fmt".padEnd(6),"layout".padEnd(12),"files");console.log("-".repeat(80));for(const row of rows){console.log(row.network.padEnd(14),row.zip.padEnd(6),row.layout.padEnd(12),row.files,row.note?` ← ${row.note}`:"")}if(failures.length){console.error("\nFAIL");failures.forEach(f=>console.error(" -",f));process.exit(1)}console.log("\nOK all",allowedAdNetworks.length,"networks");console.log("Intentional diffs: facebook, mintegral zip contain only index.html (webpack comment still says split JS).");