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

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 (92) hide show
  1. package/README.md +262 -0
  2. package/cli/bin/playable-scripts.js +53 -6
  3. package/cli/commands/build.js +241 -32
  4. package/cli/commands/builds.js +13 -2
  5. package/cli/commands/dev.js +22 -1
  6. package/cli/commands/pack.js +320 -40
  7. package/cli/commands/vite-dev.js +7 -1
  8. package/core/artifacts/emit.js +98 -1
  9. package/core/artifacts/fflate.js +31 -1
  10. package/core/artifacts/zip.js +39 -1
  11. package/core/batch-build.js +1261 -147
  12. package/core/constants.js +20 -2
  13. package/core/cos-uploader.js +289 -23
  14. package/core/engines/cocos/clean-cache.js +179 -5
  15. package/core/engines/cocos/clean-cache.test.js +75 -1
  16. package/core/engines/cocos/cli-build.js +116 -2
  17. package/core/engines/cocos/cli.js +38 -1
  18. package/core/engines/cocos/constants.js +21 -1
  19. package/core/engines/cocos/creator-cli.js +108 -1
  20. package/core/engines/cocos/dev-server.js +412 -4
  21. package/core/engines/cocos/fs.js +105 -1
  22. package/core/engines/cocos/injects/3x/init.js +438 -19
  23. package/core/engines/cocos/injects/3x/main.js +52 -2
  24. package/core/engines/cocos/injects/libs/pako.js +2 -1
  25. package/core/engines/cocos/mcp-refresh.js +76 -2
  26. package/core/engines/cocos/merge.js +147 -2
  27. package/core/engines/cocos/pack.js +187 -1
  28. package/core/engines/cocos/preview-cache.js +48 -2
  29. package/core/engines/cocos/preview-cache.test.js +32 -1
  30. package/core/engines/cocos/preview-concat.js +189 -5
  31. package/core/engines/cocos/preview-concat.test.js +73 -1
  32. package/core/engines/cocos/preview-json-hook.js +200 -1
  33. package/core/engines/cocos/preview-json-pack.js +376 -2
  34. package/core/engines/cocos/preview-json-pack.test.js +226 -2
  35. package/core/engines/cocos/preview-proxy.js +132 -1
  36. package/core/engines/cocos/rebundle-preview.js +54 -2
  37. package/core/engines/cocos/strip-social-share.js +115 -3
  38. package/core/engines/cocos/theme-assets.js +161 -2
  39. package/core/engines/cocos/writable-cli.js +248 -2
  40. package/core/engines/cocos/writable-cli.test.js +112 -1
  41. package/core/engines/web/index.js +20 -1
  42. package/core/engines/web/vite.dev.js +322 -17
  43. package/core/engines/web/webpack.build.js +522 -65
  44. package/core/engines/web/webpack.common.js +177 -11
  45. package/core/engines/web/webpack.dev.js +88 -6
  46. package/core/index.js +101 -7
  47. package/core/injectors/apply.js +94 -1
  48. package/core/injectors/check-channel-matrix.js +130 -2
  49. package/core/injectors/spec.js +217 -9
  50. package/core/loaders/gltf-loader.js +74 -28
  51. package/core/options.js +353 -16
  52. package/core/plugins/AdikteevInjectorPlugin.js +26 -4
  53. package/core/plugins/BigoAdsInjectorPlugin.js +21 -4
  54. package/core/plugins/DAPIInjectorPlugin.js +25 -2
  55. package/core/plugins/DebuggerInjectionPlugin.js +31 -3
  56. package/core/plugins/ExitAPIInjectorPlugin.js +56 -3
  57. package/core/plugins/FflateCompressionPlugin.js +231 -40
  58. package/core/plugins/FontInjectionWebpackPlugin.js +46 -4
  59. package/core/plugins/LiftoffInjectorPlugin.js +26 -4
  60. package/core/plugins/MRAIDInjectorPlugin.js +24 -2
  61. package/core/plugins/MintegralInjectorPlugin.js +25 -2
  62. package/core/plugins/PangleInjectorPlugin.js +24 -2
  63. package/core/plugins/PlaycraftOrientationInjectorPlugin.js +29 -3
  64. package/core/plugins/SnapchatInjectorPlugin.js +26 -4
  65. package/core/plugins/SocialMediaInjectorPlugin.js +119 -7
  66. package/core/plugins/TikTokInjectorPlugin.js +24 -2
  67. package/core/plugins/UnityInjectorPlugin.js +37 -8
  68. package/core/plugins/ZipPlugin.js +154 -20
  69. package/core/plugins/socialMediaVitePlugin.js +108 -2
  70. package/core/utils/buildDefines.js +30 -2
  71. package/core/utils/buildTemplateString.js +39 -5
  72. package/core/utils/date.js +16 -1
  73. package/core/utils/fontRegistry.js +81 -13
  74. package/core/utils/fontResolver.js +335 -37
  75. package/core/utils/generateAdikteevHtmlWebpackPluginConfig.js +42 -7
  76. package/core/utils/generateBigabidHtmlWebpackPluginConfig.js +30 -3
  77. package/core/utils/generateInMobiHtmlWebpackPluginConfig.js +43 -7
  78. package/core/utils/getPlaycraftOrientationSuffix.js +9 -2
  79. package/core/utils/injectSDKPlugins.js +68 -13
  80. package/core/utils/logOptions.js +46 -5
  81. package/core/utils/mergeOptions.js +21 -2
  82. package/core/utils/parseArgvOptions.js +125 -5
  83. package/core/utils/resolveChannelFold2Zip.js +15 -3
  84. package/core/utils/validateThemeData.js +227 -26
  85. package/core/validators/pre-build-checker.js +287 -25
  86. package/core/validators/tracking-validator.js +441 -40
  87. package/core/validators/tracking-validator.test.js +84 -2
  88. package/core/vite.dev.js +1 -1
  89. package/core/webpack.build.js +1 -1
  90. package/core/webpack.common.js +1 -1
  91. package/core/webpack.dev.js +1 -1
  92. package/package.json +3 -2
@@ -1,11 +1,177 @@
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
+ 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,16 +1,98 @@
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");/**
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
+ /**
2
16
  * Creates webpack configuration for development
3
17
  * @param {Partial<import('./index').CLIOptions>} [customOptions] - Custom options to merge with default options
4
18
  * @param {Record<string, any>} [customDefines] - Additional defines for webpack.DefinePlugin
5
19
  * @param {import('webpack').Configuration} [webpackCustomConfig] - Custom webpack config to merge
6
20
  * @returns {import('webpack').Configuration} Final webpack development configuration
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}/**
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
+ /**
11
83
  * Starts webpack development server
12
84
  * @param {import('webpack').Configuration} [webpackConfig] - Webpack configuration to use, creates default if not provided
13
85
  * @param {Partial<import('./index').CLIOptions>} [customOptions] - Custom options to merge with default options
14
86
  * @param {Record<string, any>} [customDefines] - Additional defines for webpack.DefinePlugin
15
87
  * @param {import('webpack').Configuration} [webpackCustomConfig] - Custom webpack config to merge
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;
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;
package/core/index.js CHANGED
@@ -1,7 +1,101 @@
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
+ 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 +1,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
+ '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,9 +1,137 @@
1
1
  #!/usr/bin/env node
2
- "use strict";/**
2
+ 'use strict';
3
+
4
+ /**
3
5
  * Compare Cocos pack plan + injections against webpack.build.js rules.
4
6
  * Run: node core/injectors/check-channel-matrix.js
5
7
  *
6
8
  * Webpack is not modified. Known intentional Cocos diffs:
7
9
  * facebook / mintegral zip = inlined index.html only
8
10
  * (webpack still skips HtmlInlineScriptPlugin for those two)
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).");
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).');