@playcraft/devkit 1.0.1-8.beta.1

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 (209) hide show
  1. package/LICENSE +21 -0
  2. package/cli/bin/playable-scripts.js +2 -0
  3. package/cli/commands/build.js +27 -0
  4. package/cli/commands/builds.js +16 -0
  5. package/cli/commands/dev.js +1 -0
  6. package/cli/commands/pack.js +81 -0
  7. package/cli/commands/vite-dev.js +1 -0
  8. package/core/batch-build.js +181 -0
  9. package/core/cos-uploader.js +49 -0
  10. package/core/index.d.ts +185 -0
  11. package/core/index.js +6 -0
  12. package/core/loaders/gltf-loader.js +45 -0
  13. package/core/options.js +26 -0
  14. package/core/plugins/AdikteevInjectorPlugin.js +47 -0
  15. package/core/plugins/BigoAdsInjectorPlugin.js +7 -0
  16. package/core/plugins/DAPIInjectorPlugin.js +5 -0
  17. package/core/plugins/DebuggerInjectionPlugin.js +6 -0
  18. package/core/plugins/ExitAPIInjectorPlugin.js +10 -0
  19. package/core/plugins/FflateCompressionPlugin.js +225 -0
  20. package/core/plugins/FontInjectionWebpackPlugin.js +17 -0
  21. package/core/plugins/LiftoffInjectorPlugin.js +14 -0
  22. package/core/plugins/MRAIDInjectorPlugin.js +4 -0
  23. package/core/plugins/MintegralInjectorPlugin.js +5 -0
  24. package/core/plugins/PangleInjectorPlugin.js +4 -0
  25. package/core/plugins/SnapchatInjectorPlugin.js +20 -0
  26. package/core/plugins/TikTokInjectorPlugin.js +4 -0
  27. package/core/plugins/UnityInjectorPlugin.js +20 -0
  28. package/core/plugins/ZipPlugin.js +19 -0
  29. package/core/resources/bigoads-config.json +1 -0
  30. package/core/resources/fonts/README.md +31 -0
  31. package/core/resources/fonts/maoken/04e60956bbe055746aa7bca5c229ca8c.woff2 +0 -0
  32. package/core/resources/fonts/maoken/086a30eeaa0f5558062389361dca8685.woff2 +0 -0
  33. package/core/resources/fonts/maoken/1b1c9545aa1da01979d240f38f979b9e.woff2 +0 -0
  34. package/core/resources/fonts/maoken/1dd31fae60d98fcad0812cb4dc54afdd.woff2 +0 -0
  35. package/core/resources/fonts/maoken/1f08966635ca0dc4a64f519863df0dcc.woff2 +0 -0
  36. package/core/resources/fonts/maoken/26aab6a388b04c37fd5a4d066a512e14.woff2 +0 -0
  37. package/core/resources/fonts/maoken/29390db71394eb0d298fa1c339590e45.woff2 +0 -0
  38. package/core/resources/fonts/maoken/2b743d9f736c5cda9250fc8b85f69db8.woff2 +0 -0
  39. package/core/resources/fonts/maoken/2f16b1f8881909c4e65404610460797f.woff2 +0 -0
  40. package/core/resources/fonts/maoken/339dc88f6fd1b09336410154252f858f.woff2 +0 -0
  41. package/core/resources/fonts/maoken/3665591d2bd9a3f31a302a34a76c74dc.woff2 +0 -0
  42. package/core/resources/fonts/maoken/3b58ece5ff16a5addc9adc1c74717d12.woff2 +0 -0
  43. package/core/resources/fonts/maoken/3e3aacd6cd2255be39c97c4ed6eb978b.woff2 +0 -0
  44. package/core/resources/fonts/maoken/3e8cdd750fcbbfcf9a1101c7418eeb36.woff2 +0 -0
  45. package/core/resources/fonts/maoken/3f71b3c0ecb555f9fa88d1b2ae0ec77f.woff2 +0 -0
  46. package/core/resources/fonts/maoken/40f956d892d89a442992737448f0330b.woff2 +0 -0
  47. package/core/resources/fonts/maoken/47ecbb41501b6cb923aaa8b8324bb22f.woff2 +0 -0
  48. package/core/resources/fonts/maoken/4a6e704952e05d217e4f4d2c2a330094.woff2 +0 -0
  49. package/core/resources/fonts/maoken/4ec9fc653a6aaabd2296fac3fe906416.woff2 +0 -0
  50. package/core/resources/fonts/maoken/5d950afcbaac4bb8f44083e5f4572957.woff2 +0 -0
  51. package/core/resources/fonts/maoken/6505b718e31b5b2b29c4f50a2a2b5fe6.woff2 +0 -0
  52. package/core/resources/fonts/maoken/673405ebfdfebae1104f36efa778714d.woff2 +0 -0
  53. package/core/resources/fonts/maoken/695a686e27152d8e81b4459b64d7b6a1.woff2 +0 -0
  54. package/core/resources/fonts/maoken/6b5fb0c960eed7a53f39adb18d0ab28b.woff2 +0 -0
  55. package/core/resources/fonts/maoken/6f53ca6c761ff540e11d5f7111dad67c.woff2 +0 -0
  56. package/core/resources/fonts/maoken/7125e9df28437d053e56f85a908a7343.woff2 +0 -0
  57. package/core/resources/fonts/maoken/731865983ae51e2a4bdc6c194da0b82e.woff2 +0 -0
  58. package/core/resources/fonts/maoken/74d993e9aa68ef636eb1287439d06bb0.woff2 +0 -0
  59. package/core/resources/fonts/maoken/797653f0a72360cd584f625972b5d217.woff2 +0 -0
  60. package/core/resources/fonts/maoken/7af5930fe5e6dade3cf31d3287d6414c.woff2 +0 -0
  61. package/core/resources/fonts/maoken/7c6fe2ee55f924f00b097da208852a34.woff2 +0 -0
  62. package/core/resources/fonts/maoken/8cff8ef36a011ca7e6d103f29b22f98b.woff2 +0 -0
  63. package/core/resources/fonts/maoken/8ff8566257c5df17ddde67b2b48243a4.woff2 +0 -0
  64. package/core/resources/fonts/maoken/93988344f6df526a0033a05d2d927d76.woff2 +0 -0
  65. package/core/resources/fonts/maoken/99d1c5647f1ce540674bd32354c2038f.woff2 +0 -0
  66. package/core/resources/fonts/maoken/a328b132aac2a3f53d97b8e7ad940e1a.woff2 +0 -0
  67. package/core/resources/fonts/maoken/a7dc00c95dd1e7a8eca8053975b0c07c.woff2 +0 -0
  68. package/core/resources/fonts/maoken/ab93ca86fb3e7ad8e95b93f7de5d8f97.woff2 +0 -0
  69. package/core/resources/fonts/maoken/b1b83e931f7e2187786b87545a6ee842.woff2 +0 -0
  70. package/core/resources/fonts/maoken/bb31b8c0ccf2d93af7cc25f19a576970.woff2 +0 -0
  71. package/core/resources/fonts/maoken/bd4a61534e4a0f426dd0d396362b211d.woff2 +0 -0
  72. package/core/resources/fonts/maoken/bec7534be141c6473023f18f5aee7a0e.woff2 +0 -0
  73. package/core/resources/fonts/maoken/c133a76c68c645b46b20e7faf9894ab6.woff2 +0 -0
  74. package/core/resources/fonts/maoken/c2deab25bd23b63c794da537e208c61a.woff2 +0 -0
  75. package/core/resources/fonts/maoken/cf13611d9749dd23fd9737073d2110f6.woff2 +0 -0
  76. package/core/resources/fonts/maoken/d01182e5dd9f51dce713916e8f872bbf.woff2 +0 -0
  77. package/core/resources/fonts/maoken/d1d4c88b2a14e679d3cc9d0ad8e8abdb.woff2 +0 -0
  78. package/core/resources/fonts/maoken/df61c7f5fa45aa6156122c09653f6700.woff2 +0 -0
  79. package/core/resources/fonts/maoken/e1867d1454d8c903ba29bac70f88c211.woff2 +0 -0
  80. package/core/resources/fonts/maoken/ef912f41f9109c5d936005af5552f521.woff2 +0 -0
  81. package/core/resources/fonts/maoken/f0f119744871bb6bec7e0eef4e1ce8ba.woff2 +0 -0
  82. package/core/resources/fonts/maoken/f32a268245b50f6c19bd946239fd8cc0.woff2 +0 -0
  83. package/core/resources/fonts/maoken/f4735851e08da122f471100ecff5c4ae.woff2 +0 -0
  84. package/core/resources/fonts/maoken/f85b8f6994600622f767506ae55d6280.woff2 +0 -0
  85. package/core/resources/fonts/maoken/fc145bdf9c20b67db44156382155b465.woff2 +0 -0
  86. package/core/resources/fonts/maoken/fcd8571ec88b953c893d77c4fd5c6ebc.woff2 +0 -0
  87. package/core/resources/fonts/maoken/fcdcb3f4b26b5c1f45fdd151f45e0c6d.woff2 +0 -0
  88. package/core/resources/fonts/maoken/index.json +59 -0
  89. package/core/resources/fonts/noto-sans-sc-regular.woff2 +0 -0
  90. package/core/resources/fonts/pangmen/0047cd3053f8c06117a653b73e100391.woff2 +0 -0
  91. package/core/resources/fonts/pangmen/008870151036457b39ee8794f5bc7a64.woff2 +0 -0
  92. package/core/resources/fonts/pangmen/01b5355e45274055ad794cc1b1305e69.woff2 +0 -0
  93. package/core/resources/fonts/pangmen/0756361ea2445ec1c500d354fa0867d0.woff2 +0 -0
  94. package/core/resources/fonts/pangmen/091cf1201fbbda8dc98c01e775991bab.woff2 +0 -0
  95. package/core/resources/fonts/pangmen/0c854072dc9998bd02ea8b29b5f1f55e.woff2 +0 -0
  96. package/core/resources/fonts/pangmen/10f3f7f8da6ab5f882e816c97532a4c6.woff2 +0 -0
  97. package/core/resources/fonts/pangmen/141229a7039c633c894ea5168d0ae849.woff2 +0 -0
  98. package/core/resources/fonts/pangmen/1b0b982276f5f93b3c71b3f6b1b81184.woff2 +0 -0
  99. package/core/resources/fonts/pangmen/1e415e51c4efb3182b87665d726fc423.woff2 +0 -0
  100. package/core/resources/fonts/pangmen/1f5fb0c8d785813dc765ff889b491361.woff2 +0 -0
  101. package/core/resources/fonts/pangmen/1fe8065222947d67d4177fe3bdd782cd.woff2 +0 -0
  102. package/core/resources/fonts/pangmen/229674de0b8d208f607468537d10ca48.woff2 +0 -0
  103. package/core/resources/fonts/pangmen/27230f128a3708090bed2eb5bb6ad437.woff2 +0 -0
  104. package/core/resources/fonts/pangmen/2d8f78526cd5d63e22274b3f0e5c4e50.woff2 +0 -0
  105. package/core/resources/fonts/pangmen/2f441859584ca1d060378be9f1173a6a.woff2 +0 -0
  106. package/core/resources/fonts/pangmen/319ed7be5128268f3cf297464b305a55.woff2 +0 -0
  107. package/core/resources/fonts/pangmen/35a41344eec91ef694ca59fda4dc9ccc.woff2 +0 -0
  108. package/core/resources/fonts/pangmen/3715cbec78511ff67e44fdd363b5fd79.woff2 +0 -0
  109. package/core/resources/fonts/pangmen/37d468e8e898644f8fddc9f83f04a5b1.woff2 +0 -0
  110. package/core/resources/fonts/pangmen/384b560d66242e5c1062489020d8749c.woff2 +0 -0
  111. package/core/resources/fonts/pangmen/38ef10dd61436ebaeb8dada7b827595c.woff2 +0 -0
  112. package/core/resources/fonts/pangmen/3e8bdfd8597f723100ff7d6b54e803f9.woff2 +0 -0
  113. package/core/resources/fonts/pangmen/43d57e12e1ec85c68a289d88f920f62e.woff2 +0 -0
  114. package/core/resources/fonts/pangmen/4c4ee1006529ddcc67e7c195d5ddb2e0.woff2 +0 -0
  115. package/core/resources/fonts/pangmen/4f65210dd5014352f4c6eb1a791e56c9.woff2 +0 -0
  116. package/core/resources/fonts/pangmen/5179f5b92b71d655b9094b282ca39161.woff2 +0 -0
  117. package/core/resources/fonts/pangmen/5282fc1cc80a878d744621a36279a1fa.woff2 +0 -0
  118. package/core/resources/fonts/pangmen/57604d7bad494a118e0fa9a6c0f1035a.woff2 +0 -0
  119. package/core/resources/fonts/pangmen/5891eaedeb9999f6b71434efc42407db.woff2 +0 -0
  120. package/core/resources/fonts/pangmen/592e3b1817378c607c8b6b183c89019a.woff2 +0 -0
  121. package/core/resources/fonts/pangmen/5c7c16778cf226e25ef39e475a19afa0.woff2 +0 -0
  122. package/core/resources/fonts/pangmen/5eccbf1ee87f73db786ee0a8441df8a3.woff2 +0 -0
  123. package/core/resources/fonts/pangmen/606a4091be7a3307a2d9adc4d2206b04.woff2 +0 -0
  124. package/core/resources/fonts/pangmen/62da615f3cbb7d19ce02973b009ec5b9.woff2 +0 -0
  125. package/core/resources/fonts/pangmen/63f9972982226af9df16006408160e8a.woff2 +0 -0
  126. package/core/resources/fonts/pangmen/6702bbfff577fce5bc621a91fdcb78bc.woff2 +0 -0
  127. package/core/resources/fonts/pangmen/6cd32dbf440a9b7711cc293b19f4c2d7.woff2 +0 -0
  128. package/core/resources/fonts/pangmen/6ef49a0811c0a0892dfbca6602365e6f.woff2 +0 -0
  129. package/core/resources/fonts/pangmen/7194cb1fa135d58fee1611bb8915fe25.woff2 +0 -0
  130. package/core/resources/fonts/pangmen/71baf3daab3248b0748dd187ab2fbc45.woff2 +0 -0
  131. package/core/resources/fonts/pangmen/7457db009eff249ec587c7d912c5d347.woff2 +0 -0
  132. package/core/resources/fonts/pangmen/762d1c7a28837c08180123e98dbe8cbf.woff2 +0 -0
  133. package/core/resources/fonts/pangmen/76e3d855d8684a1ee4d12cf7a572ca78.woff2 +0 -0
  134. package/core/resources/fonts/pangmen/78a97efdf675a6dcf903b4725de01219.woff2 +0 -0
  135. package/core/resources/fonts/pangmen/7b42a8cbf34bd2a0820ed81bb4730b6d.woff2 +0 -0
  136. package/core/resources/fonts/pangmen/7d07b13e8fc6502f0d2e21a7bb87ddb1.woff2 +0 -0
  137. package/core/resources/fonts/pangmen/7e710c007a48e4410861c67a9a26d4fc.woff2 +0 -0
  138. package/core/resources/fonts/pangmen/7eb8fe8685f6f2d6f0e223337f3a2b2b.woff2 +0 -0
  139. package/core/resources/fonts/pangmen/7ec1b00b541c2c2817cf352e2634bf5b.woff2 +0 -0
  140. package/core/resources/fonts/pangmen/7ffdd7059669ad52af7995b515889eeb.woff2 +0 -0
  141. package/core/resources/fonts/pangmen/818c6b2f12623b5a43d1036cb7f71b61.woff2 +0 -0
  142. package/core/resources/fonts/pangmen/81983731abfefd6a400b83b93d9e3665.woff2 +0 -0
  143. package/core/resources/fonts/pangmen/821ad173f8fd8eccd214241cc651657b.woff2 +0 -0
  144. package/core/resources/fonts/pangmen/83ee6dce60a5f156da3ad18e5cfb3e6e.woff2 +0 -0
  145. package/core/resources/fonts/pangmen/87db73da676a2e7b3e640e84187a5c86.woff2 +0 -0
  146. package/core/resources/fonts/pangmen/89b2edc17dd7ab4332f18b038d268068.woff2 +0 -0
  147. package/core/resources/fonts/pangmen/8b3f89232252b80f6e12ab91d93fda96.woff2 +0 -0
  148. package/core/resources/fonts/pangmen/928a7b39a7c4efdca10f70dfcdb917ac.woff2 +0 -0
  149. package/core/resources/fonts/pangmen/945d03601ca6e8be5f7e6043ecb716a5.woff2 +0 -0
  150. package/core/resources/fonts/pangmen/97de16fa122ebcbff22956b7b2de2f77.woff2 +0 -0
  151. package/core/resources/fonts/pangmen/9882677dd482462a6c1c0a1d7efbb15d.woff2 +0 -0
  152. package/core/resources/fonts/pangmen/9c46188cc1e1711f860fa06d2c29dcc1.woff2 +0 -0
  153. package/core/resources/fonts/pangmen/a279650fd2f2f115ab30cbbf748f85f9.woff2 +0 -0
  154. package/core/resources/fonts/pangmen/a3bfd69906b38ab891501ac997bd577d.woff2 +0 -0
  155. package/core/resources/fonts/pangmen/a4cc4f73c30d1dca3f7abd656705e239.woff2 +0 -0
  156. package/core/resources/fonts/pangmen/a54d94633b077f678d15b96ad9e6a9c9.woff2 +0 -0
  157. package/core/resources/fonts/pangmen/ab9bd513cba626dee079d3f8f35da118.woff2 +0 -0
  158. package/core/resources/fonts/pangmen/b4b057c12d1e44681bdb5c5e92f8e070.woff2 +0 -0
  159. package/core/resources/fonts/pangmen/b8c1e4b1ab3beb334fa773a50c9221b0.woff2 +0 -0
  160. package/core/resources/fonts/pangmen/b93617eda1294f0696193c5237320892.woff2 +0 -0
  161. package/core/resources/fonts/pangmen/bac50a33e26632667ed330c817b37dcf.woff2 +0 -0
  162. package/core/resources/fonts/pangmen/c31e6efe815ccefa90bebc1fa64aba8b.woff2 +0 -0
  163. package/core/resources/fonts/pangmen/c75ff5aa44a05db009b0a38d05f4040b.woff2 +0 -0
  164. package/core/resources/fonts/pangmen/ca70255a9a388b3f33cff49d5abc31f2.woff2 +0 -0
  165. package/core/resources/fonts/pangmen/cb249c8c31a2bee3d6a68fd1c74c061a.woff2 +0 -0
  166. package/core/resources/fonts/pangmen/cb898d32532efe5a7933672228bc620c.woff2 +0 -0
  167. package/core/resources/fonts/pangmen/cc3c33c20e8abd03b9e5acc637985d63.woff2 +0 -0
  168. package/core/resources/fonts/pangmen/cca20bec40d1a11ede1ec496d22cd026.woff2 +0 -0
  169. package/core/resources/fonts/pangmen/cedee24be6ea34e35c53c0ce5edd7af0.woff2 +0 -0
  170. package/core/resources/fonts/pangmen/d42c580643039640037f5c63750ac711.woff2 +0 -0
  171. package/core/resources/fonts/pangmen/dbe1c94b8d92f8915c33fff55b625716.woff2 +0 -0
  172. package/core/resources/fonts/pangmen/dced2d67eceeefa7f45ba44854174b8b.woff2 +0 -0
  173. package/core/resources/fonts/pangmen/dee98b9b0545a42fd3aaa87ec1fa213e.woff2 +0 -0
  174. package/core/resources/fonts/pangmen/e6ac6bb1654d431fe62556a342b16e52.woff2 +0 -0
  175. package/core/resources/fonts/pangmen/eb9be6e3e450bf5706308fcec576c6df.woff2 +0 -0
  176. package/core/resources/fonts/pangmen/ee8cb027810ba5b5d9016774789f747b.woff2 +0 -0
  177. package/core/resources/fonts/pangmen/f0ecb29938306af8e9117a24eefad319.woff2 +0 -0
  178. package/core/resources/fonts/pangmen/f1a1a697d45d7f13a71a0a38424a98a7.woff2 +0 -0
  179. package/core/resources/fonts/pangmen/f203894451810a009d6795f1b0493989.woff2 +0 -0
  180. package/core/resources/fonts/pangmen/f53b808c0d5afb527499049ba225198e.woff2 +0 -0
  181. package/core/resources/fonts/pangmen/f6f69823eda331486d1d1a6e676e1d4a.woff2 +0 -0
  182. package/core/resources/fonts/pangmen/fb1ad0e1ba9ca84b66aa88867ce4109c.woff2 +0 -0
  183. package/core/resources/fonts/pangmen/index.json +95 -0
  184. package/core/resources/fonts/poppins-regular.woff2 +0 -0
  185. package/core/resources/snapchat-config.json +1 -0
  186. package/core/resources/tiktok-config.json +1 -0
  187. package/core/utils/buildDefines.js +3 -0
  188. package/core/utils/buildTemplateString.js +6 -0
  189. package/core/utils/date.js +1 -0
  190. package/core/utils/fontRegistry.js +28 -0
  191. package/core/utils/fontResolver.js +97 -0
  192. package/core/utils/generateAdikteevHtmlWebpackPluginConfig.js +36 -0
  193. package/core/utils/generateBigabidHtmlWebpackPluginConfig.js +13 -0
  194. package/core/utils/generateInMobiHtmlWebpackPluginConfig.js +36 -0
  195. package/core/utils/injectSDKPlugins.js +19 -0
  196. package/core/utils/logOptions.js +5 -0
  197. package/core/utils/mergeOptions.js +6 -0
  198. package/core/utils/parseArgvOptions.js +8 -0
  199. package/core/utils/resolveChannelFold2Zip.js +14 -0
  200. package/core/utils/validateThemeData.js +97 -0
  201. package/core/validators/index.d.ts +242 -0
  202. package/core/validators/pre-build-checker.js +89 -0
  203. package/core/validators/tracking-validator.js +113 -0
  204. package/core/vite.dev.js +27 -0
  205. package/core/webpack.build.js +71 -0
  206. package/core/webpack.common.js +11 -0
  207. package/core/webpack.dev.js +15 -0
  208. package/defines.d.ts +54 -0
  209. package/package.json +85 -0
@@ -0,0 +1,36 @@
1
+ "use strict";const fs=require("fs");const path=require("path");/**
2
+ * InMobi 追踪宏定义
3
+ * 文档: https://support.inmobi.com/dsp/in/inmobi-dsp-india/creative-specifications-and-guidelines/
4
+ *
5
+ * InMobi 要求:
6
+ * 1. 单个 index.html 文件(所有资源内联)或 ZIP 包
7
+ * 2. 必须包含 INMOBI_DSPMACROS 追踪变量
8
+ * 3. 引用外部 mraid.js(由 InMobi 平台提供)
9
+ * 4. 支持 MRAID 3.0 标准
10
+ */const InMobiPlaceholders=`<script>
11
+ var INMOBI_DSPMACROS = {
12
+ "Ad_Load_Start": ["$P_AD_LOAD_START"],
13
+ "Ad_Viewable": ["$P_AD_READY"],
14
+ "First_Engagement": ["$P_FIRST_ENGAGEMENT"],
15
+ "Gameplay_Complete": ["$P_GAMEPLAY_DONE"],
16
+ "DSP_Click": ["$HTML_ESC_CLICK_URL"],
17
+ "LandingPage_URL": [/android/i.test(navigator.userAgent) ? "{GOOGLE_PLAY_URL}" : "{APP_STORE_URL}"],
18
+ "Spent_5_Seconds": ["$P_TIMESPENT_5"],
19
+ "Spent_10_Seconds": ["$P_TIMESPENT_10"],
20
+ "Spent_15_Seconds": ["$P_TIMESPENT_15"],
21
+ "Spent_20_Seconds": ["$P_TIMESPENT_20"],
22
+ "Spent_25_Seconds": ["$P_TIMESPENT_25"],
23
+ "Spent_30_Seconds": ["$P_TIMESPENT_30"]
24
+ };
25
+ </script>
26
+ <script src="mraid.js"></script>`;/**
27
+ * 生成 InMobi 平台所需的 HTML 配置
28
+ *
29
+ * 策略:
30
+ * 1. 使用标准的 HtmlWebpackPlugin 配置(不是 templateContent)
31
+ * 2. JS 会通过 HtmlInlineScriptPlugin 自动内联到 HTML 中
32
+ * 3. 通过修改 template 文件注入 InMobi 追踪脚本
33
+ */exports.generateInMobiHtmlWebpackPluginConfig=function generateInMobiHtmlWebpackPluginConfig(originalHtmlContentPath,buildOptions){originalHtmlContentPath=originalHtmlContentPath||"src/index.html";const placeholder=InMobiPlaceholders.replaceAll("{GOOGLE_PLAY_URL}",buildOptions.googlePlayUrl).replaceAll("{APP_STORE_URL}",buildOptions.appStoreUrl);// 读取原始 HTML 模板
34
+ let htmlContent="";try{htmlContent=fs.readFileSync(path.resolve(originalHtmlContentPath),"utf8")}catch(e){console.warn("[InMobi] Could not read HTML template:",e.message)}// 在 </head> 之前注入 InMobi 追踪脚本
35
+ htmlContent=htmlContent.replace("</head>",`${placeholder}\n</head>`);return{filename:"index.html",inject:"head",// webpack 会自动注入 JS,然后 HtmlInlineScriptPlugin 内联
36
+ minify:false,templateContent:htmlContent,meta:{charset:"UTF-8",viewport:"width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no"}}};
@@ -0,0 +1,19 @@
1
+ "use strict";/**
2
+ * 根据 AD_NETWORK 和 AD_PROTOCOL 注入对应的渠道 SDK 脚本插件
3
+ * 此函数被 webpack.dev.js 和 webpack.build.js 共用
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");/**
5
+ * 注入渠道 SDK 插件到 webpack 配置中
6
+ * @param {import('webpack').Configuration} webpackConfig - webpack 配置对象
7
+ * @param {Object} options - 配置选项
8
+ * @param {AD_NETWORK} options.network - 广告网络
9
+ * @param {AD_PROTOCOL} options.protocol - 广告协议
10
+ * @param {string} [options.orientation] - 屏幕方向(google 渠道需要)
11
+ */function injectSDKPlugins(webpackConfig,options){const{network:adNetwork,protocol:adProtocol,orientation}=options;// 协议层注入
12
+ if("dapi"===adProtocol){webpackConfig.plugins.push(new DAPIInjectorPlugin)}else if("mraid"===adProtocol){// 只有 ironsource 才注入 MRAIDInjectorPlugin
13
+ // Unity 使用专门的 UnityInjectorPlugin(在渠道特定注入中处理)
14
+ if(adNetwork==="ironsource"){webpackConfig.plugins.push(new MRAIDInjectorPlugin)}}// 渠道特定注入
15
+ if(adNetwork==="google"){webpackConfig.plugins.push(new ExitAPIInjectorPlugin(orientation))}else if(adNetwork==="unity"){// Unity Ads 使用专门的 UnityInjectorPlugin
16
+ 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 模式下注入模拟配置
17
+ webpackConfig.plugins.push(new BigoAdsInjectorPlugin)}else if(adNetwork==="liftoff"){webpackConfig.plugins.push(new LiftoffInjectorPlugin)}else if(adNetwork==="snapchat"){// Snapchat 注入 ScPlayableAd SDK
18
+ webpackConfig.plugins.push(new SnapchatInjectorPlugin)}else if(adNetwork==="adikteev"){// Adikteev 注入 MRAID mock 和占位符变量
19
+ webpackConfig.plugins.push(new AdikteevInjectorPlugin)}}exports.injectSDKPlugins=injectSDKPlugins;
@@ -0,0 +1,5 @@
1
+ "use strict";var prettyjson=require("prettyjson");/**
2
+ * Log provided options into console output
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;if(logOptions.orientation==="both")delete logOptions.orientation;// 在 dev 模式下,如果 network 为默认值 preview 则不显示
4
+ if(!isProduction&&logOptions.network==="preview")delete logOptions.network;// 在 dev 模式下,如果 protocol 为默认值 none 则不显示
5
+ 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))};
@@ -0,0 +1,6 @@
1
+ "use strict";/**
2
+ * Merges two option objects with deep merge for 'defines' and 'build' properties
3
+ * @param {import('../index').CLIOptions} options1 - First options object
4
+ * @param {Partial<import('../index').CLIOptions>} [options2] - Second options object
5
+ * @returns {import('../index').CLIOptions} Merged options object with combined defines and build properties
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 options=Object.assign({},options1,options2);options.defines=defines;options.compilation=compilation;options.adNetworkNames=adNetworkNames;options.build=build;return options};
@@ -0,0 +1,8 @@
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"];/** @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"];/** @type {LANGUAGE[]} */const allowedLanguages=["auto","en","es","zh","hi","ar","fr","de","ja","pt"];/** @type {ORIENTATION[]} */const allowedOrientations=["both","portrait","landscape","square"];/**
2
+ * Parses command line arguments based on provided options configuration
3
+ * @param {import('../index').CLIOptionConfig[]} posiibleOptions - Array of possible options to parse
4
+ * @returns {Record<string, any>} Parsed options object with values from command line arguments
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.allowedOrientations=allowedOrientations;
@@ -0,0 +1,14 @@
1
+ "use strict";/**
2
+ * 解析 isChannelFold2Zip 相关标志
3
+ *
4
+ * isChannelFold2Zip:所有渠道(排除 preview)输出 dist/渠道名称.zip
5
+ * - ZIP 渠道(google/tiktok 等):正常 ZIP 流程,输出路径改为 dist/渠道名称.zip
6
+ * - 非 ZIP 渠道(facebook 默认等):强制将 HTML 打包成 zip,输出 dist/渠道名称.zip
7
+ *
8
+ * @param {object} params
9
+ * @param {boolean} params.isChannelFold2ZipOption - buildOptions.isChannelFold2Zip 的值
10
+ * @param {string} params.adNetwork - 当前渠道名称
11
+ * @param {boolean} params.isZipOutput - 当前渠道是否本身就走 ZIP 输出流程
12
+ * @returns {{ isChannelFold2Zip: boolean, isZipOutputFinal: boolean }}
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;
@@ -0,0 +1,97 @@
1
+ "use strict";const fs=require("fs");const path=require("path");const JSON5=require("json5");const Ajv=require("ajv");const SCHEMA_BACKUP_SUFFIX=".backup";const SCHEMA_BACKUP_COMMENT="// [\u81EA\u52A8\u751F\u6210\u7684\u5907\u4EFD] \u6B64\u6587\u4EF6\u7531\u6253\u5305\u6D41\u7A0B\u521B\u5EFA\u3002\n"+"// \u5982\u679C\u6253\u5305\u4E2D\u65AD\uFF0C\u53EF\u4EE5\u79FB\u9664 \".backup\" \u540E\u7F00\uFF0C\n"+"// \u5E76\u7528\u6B64\u6587\u4EF6\u66FF\u6362\u539F\u59CB\u7684 \"theme.schema.json5\" \u6765\u6062\u590D\u3002\n";/**
2
+ * 递归提取 JSON Schema 定义中的 `default` 值。
3
+ *
4
+ * - 对于 "object" 类型:递归遍历 `properties` 并构建嵌套对象。
5
+ * - 对于基本类型:如果存在 `default` 值则返回。
6
+ *
7
+ * @param {object} schema - JSON Schema(或子 schema)对象
8
+ * @returns {*} 提取的默认值树,如果没有则返回 undefined
9
+ */function extractDefaultsFromSchema(schema){if(!schema||typeof schema!=="object")return undefined;if(schema.type==="object"&&schema.properties){const result={};for(const[key,propSchema]of Object.entries(schema.properties)){const val=extractDefaultsFromSchema(propSchema);if(val!==undefined){result[key]=val}}return Object.keys(result).length>0?result:undefined}// 基本类型或数组类型,带有默认值
10
+ if("default"in schema){return schema.default}return undefined}/**
11
+ * Check whether the parsed content of theme.schema.json5 is a real JSON Schema
12
+ * (has "type" and "properties" fields) or has already been overwritten with
13
+ * plain merged data (e.g. due to a previous interrupted build).
14
+ *
15
+ * @param {object} obj - Parsed content of theme.schema.json5
16
+ * @returns {boolean} true if it looks like a valid JSON Schema definition
17
+ */function isJsonSchema(obj){return obj&&typeof obj==="object"&&obj.type==="object"&&typeof obj.properties==="object"}/**
18
+ * 根据 schema 校验指定主题的主题数据。
19
+ *
20
+ * 项目中预期的文件结构:
21
+ * src/theme/theme.schema.json5 – JSON Schema(JSON5 格式)
22
+ * src/theme/{themeName}/theme.data.json5 – 各主题的数据
23
+ *
24
+ * 通过 extractDefaultsFromSchema 从 schema 的 `default` 字段提取默认值,
25
+ * 然后与主题数据深度合并(主题数据优先)后再进行校验。
26
+ *
27
+ * 保险机制:如果 theme.schema.json5 已经是纯 JSON 数据(非 schema 格式),
28
+ * 说明上次构建中断后未能恢复,此时直接使用该数据,跳过校验。
29
+ *
30
+ * @param {string} themeName - 主题目录名称
31
+ * @param {string} projectRoot - 项目根目录的绝对路径
32
+ * @param {object} [themeConfig] - 可选的主题配置(builds.config.js 中的 themes 部分)
33
+ * @returns {{ defaultData: object, mergedData: object, isPlainData: boolean, errors: string[] | null }}
34
+ * defaultData – 从 schema 提取的默认值
35
+ * mergedData – schema 默认值与主题数据合并后的完整数据
36
+ * isPlainData – 如果 schema 文件已经是纯数据(非 schema 格式)则为 true
37
+ * errors – 校验通过为 null,否则为错误信息数组
38
+ */function validateThemeData(themeName,projectRoot,themeConfig){const sourceDir=themeConfig&&themeConfig.sourceDir||"src/theme";const themeBaseDir=path.join(projectRoot,sourceDir);// --- 解析文件路径 ---
39
+ const schemaPath=path.join(themeBaseDir,"theme.schema.json5");const dataPath=path.join(themeBaseDir,themeName,"theme.data.json5");// --- 读取并解析文件 ---
40
+ if(!fs.existsSync(schemaPath)){return{defaultData:null,mergedData:null,isPlainData:false,errors:[`未找到 Schema 文件: ${schemaPath}`]}}const schemaContent=JSON5.parse(fs.readFileSync(schemaPath,"utf8"));// Safety check: if the file is already plain data (not a JSON Schema),
41
+ // it was likely left over from a previous interrupted build.
42
+ // Use it directly without validation or overwriting.
43
+ if(!isJsonSchema(schemaContent)){return{defaultData:schemaContent,mergedData:schemaContent,isPlainData:true,errors:null}}// 从 schema 提取默认值
44
+ const defaultData=extractDefaultsFromSchema(schemaContent)||{};let themeData={};if(fs.existsSync(dataPath)){themeData=JSON5.parse(fs.readFileSync(dataPath,"utf8"))}// 深度合并(主题数据覆盖 schema 默认值),然后校验
45
+ const merged=deepMerge(defaultData,themeData);// --- 根据 schema 校验合并后的数据 ---
46
+ const ajv=new Ajv({allErrors:true,strict:false});const validate=ajv.compile(schemaContent);const valid=validate(merged);if(!valid){const errors=validate.errors.map(err=>{const field=err.instancePath||"(root)";return`${field} ${err.message}`});return{defaultData,mergedData:merged,isPlainData:false,errors}}return{defaultData,mergedData:merged,isPlainData:false,errors:null}}/**
47
+ * 深度合并两个普通对象。`override` 中的值优先。
48
+ * 数组会被整体替换(不会拼接)。
49
+ *
50
+ * @param {object} base
51
+ * @param {object} override
52
+ * @returns {object}
53
+ */function deepMerge(base,override){const result={...base};for(const key of Object.keys(override)){const baseVal=base[key];const overVal=override[key];if(overVal!==null&&typeof overVal==="object"&&!Array.isArray(overVal)&&baseVal!==null&&typeof baseVal==="object"&&!Array.isArray(baseVal)){result[key]=deepMerge(baseVal,overVal)}else{result[key]=overVal}}return result}/**
54
+ * 备份原始的 theme.schema.json5 文件。
55
+ * 备份文件包含注释头,说明如何手动恢复。
56
+ *
57
+ * @param {string} schemaPath - theme.schema.json5 的绝对路径
58
+ * @returns {string} 备份文件路径
59
+ */function backupSchema(schemaPath){const backupPath=schemaPath+SCHEMA_BACKUP_SUFFIX;const originalContent=fs.readFileSync(schemaPath,"utf8");fs.writeFileSync(backupPath,SCHEMA_BACKUP_COMMENT+originalContent,"utf8");return backupPath}/**
60
+ * 从备份文件恢复 theme.schema.json5 并删除备份。
61
+ *
62
+ * @param {string} schemaPath - theme.schema.json5 的绝对路径
63
+ */function restoreSchema(schemaPath){const backupPath=schemaPath+SCHEMA_BACKUP_SUFFIX;if(!fs.existsSync(backupPath))return;const backupContent=fs.readFileSync(backupPath,"utf8");// 去除自动生成的注释头(以 "//" 开头的行)
64
+ const lines=backupContent.split("\n");const contentStart=lines.findIndex(line=>!line.startsWith("//"));const originalContent=lines.slice(contentStart>=0?contentStart:0).join("\n");fs.writeFileSync(schemaPath,originalContent,"utf8");fs.unlinkSync(backupPath)}/**
65
+ * 用合并后的数据(schema 默认值 + 主题覆盖值)覆写 theme.schema.json5,
66
+ * 以便打包工具在构建时可以直接使用。
67
+ *
68
+ * @param {string} schemaPath - theme.schema.json5 的绝对路径
69
+ * @param {object} mergedData - 要写入的合并后的主题数据
70
+ */function overwriteSchemaWithMerged(schemaPath,mergedData){const content=JSON.stringify(mergedData,null,2);fs.writeFileSync(schemaPath,content,"utf8")}/**
71
+ * 执行主题校验并输出标准日志。
72
+ * 单次构建(build.js)和批量构建(batch-build.js)共用此函数。
73
+ *
74
+ * 流程:
75
+ * 1. 根据 theme.schema.json5 校验 theme.data.json5
76
+ * 2. 备份 theme.schema.json5(包含恢复说明)
77
+ * 3. 通过 extractDefaultsFromSchema 从 schema 提取默认值
78
+ * 4. 用默认数据覆写 theme.schema.json5 以供构建使用
79
+ *
80
+ * 构建完成后(无论成功或失败),调用 restoreSchema() 恢复原始文件。
81
+ *
82
+ * @param {string} themeName - 主题目录名称
83
+ * @param {string} projectRoot - 项目根目录的绝对路径
84
+ * @param {object} [themeConfig] - 可选的主题配置(builds.config.js 中的 themes 部分)
85
+ * @param {object} [options]
86
+ * @param {function} [options.log] - 自定义日志函数(message, level)。默认使用 console.log。
87
+ * @returns {{ valid: boolean, errors: string[]|null, schemaPath: string|null }}
88
+ * valid – schema 文件不存在(跳过)或校验通过时为 true
89
+ * errors – 校验通过为 null,否则为错误信息数组
90
+ * schemaPath – theme.schema.json5 的绝对路径(用于 restoreSchema 调用),或 null
91
+ */function runThemeValidation(themeName,projectRoot,themeConfig,options){const sourceDir=themeConfig&&themeConfig.sourceDir||"src/theme";const themeBaseDir=path.join(projectRoot,sourceDir);const schemaPath=path.join(themeBaseDir,"theme.schema.json5");// 没有 schema 文件 → 完全跳过校验
92
+ if(!fs.existsSync(schemaPath)){return{valid:true,errors:null,schemaPath:null}}const logFn=options&&options.log||function defaultLog(msg,level){const colors={info:"\x1B[36m",success:"\x1B[32m",error:"\x1B[31m",warn:"\x1B[33m"};const reset="\x1B[0m";const color=colors[level]||"";console.log(`${color}${msg}${reset}`)};logFn(`校验主题数据: ${themeName}`,"info");const{defaultData,mergedData,isPlainData,errors}=validateThemeData(themeName,projectRoot,themeConfig);// Safety: schema file is already plain data (previous build was interrupted)
93
+ // Use it directly, no need to backup/overwrite
94
+ if(isPlainData){logFn(`⚠️ theme.schema.json5 已经是纯数据格式(可能上次构建中断未恢复),直接使用`,"warn");// schemaPath is returned as null so that restoreSchema won't be called
95
+ // (there's nothing to restore — the file is already data, not a schema)
96
+ return{valid:true,errors:null,schemaPath:null}}if(errors){logFn(`主题 "${themeName}" 数据校验失败:`,"error");errors.forEach(e=>logFn(` - ${e}`,"error"));return{valid:false,errors,schemaPath}}logFn(`主题 "${themeName}" 数据校验通过`,"success");// 备份原始 schema,然后用合并后的数据覆写以供构建使用
97
+ backupSchema(schemaPath);overwriteSchemaWithMerged(schemaPath,mergedData);logFn(`已备份 schema 并写入合并后的主题数据`,"info");return{valid:true,errors:null,schemaPath}}module.exports={validateThemeData,deepMerge,extractDefaultsFromSchema,isJsonSchema,runThemeValidation,backupSchema,restoreSchema,overwriteSchemaWithMerged};
@@ -0,0 +1,242 @@
1
+ // ============================================================
2
+ // 埋点验证器类型定义
3
+ // ============================================================
4
+
5
+ /**
6
+ * 埋点方法配置
7
+ */
8
+ export interface TrackingMethod {
9
+ /** 方法名 */
10
+ name: string;
11
+ /** 是否必须调用 */
12
+ required: boolean;
13
+ /** 方法描述 */
14
+ description: string;
15
+ /** 对应的渠道埋点 */
16
+ channels: string[];
17
+ }
18
+
19
+ /**
20
+ * 验证结果
21
+ */
22
+ export interface ValidationResult {
23
+ /** 验证是否通过 */
24
+ success: boolean;
25
+ /** 集成类型 */
26
+ integrationType: 'sdk' | 'tracking' | 'none';
27
+ /** 方法检测结果 */
28
+ methods: Array<{
29
+ method: string;
30
+ found: boolean;
31
+ required: boolean;
32
+ description: string;
33
+ }>;
34
+ /** 错误信息列表 */
35
+ errors: string[];
36
+ /** 警告信息列表 */
37
+ warnings: string[];
38
+ /** 统计信息 */
39
+ stats: {
40
+ totalFiles: number;
41
+ scannedFiles: number;
42
+ hasTrackingImport: boolean;
43
+ hasSDKImport: boolean;
44
+ };
45
+ }
46
+
47
+ /**
48
+ * 验证选项
49
+ */
50
+ export interface ValidationOptions {
51
+ /** 文件扩展名列表 */
52
+ extensions?: string[];
53
+ /** 排除的目录列表 */
54
+ excludeDirs?: string[];
55
+ /** 是否输出详细信息 */
56
+ verbose?: boolean;
57
+ }
58
+
59
+ /**
60
+ * 验证项目中的埋点调用
61
+ * @param projectRoot - 项目根目录
62
+ * @param options - 验证选项
63
+ */
64
+ export declare function validateTracking(
65
+ projectRoot: string,
66
+ options?: ValidationOptions
67
+ ): ValidationResult;
68
+
69
+ /**
70
+ * 格式化验证结果并输出到控制台
71
+ * @param result - 验证结果
72
+ * @param showDetails - 是否显示详细信息
73
+ */
74
+ export declare function printValidationResult(
75
+ result: ValidationResult,
76
+ showDetails?: boolean
77
+ ): void;
78
+
79
+ /**
80
+ * 扫描目录获取所有源码文件
81
+ * @param dir - 目录路径
82
+ * @param extensions - 文件扩展名列表
83
+ * @param excludeDirs - 排除的目录名列表
84
+ */
85
+ export declare function scanSourceFiles(
86
+ dir: string,
87
+ extensions?: string[],
88
+ excludeDirs?: string[]
89
+ ): string[];
90
+
91
+ /**
92
+ * 检测代码中是否包含 tracking 方法的直接调用
93
+ * @param content - 文件内容
94
+ * @param methodName - 方法名
95
+ */
96
+ export declare function detectDirectTrackingCall(
97
+ content: string,
98
+ methodName: string
99
+ ): boolean;
100
+
101
+ /**
102
+ * 检测代码中是否包含 SDK 方法调用
103
+ * @param content - 文件内容
104
+ * @param sdkMethod - SDK 方法名
105
+ */
106
+ export declare function detectSDKCall(
107
+ content: string,
108
+ sdkMethod: string
109
+ ): boolean;
110
+
111
+ /**
112
+ * 检测是否导入了 @playcraft/ads-tracking
113
+ * @param content - 文件内容
114
+ */
115
+ export declare function hasTrackingImport(content: string): boolean;
116
+
117
+ /**
118
+ * 检测是否导入了 @playcraft/adsdk
119
+ * @param content - 文件内容
120
+ */
121
+ export declare function hasSDKImport(content: string): boolean;
122
+
123
+ /**
124
+ * 所有埋点方法配置
125
+ */
126
+ export declare const TRACKING_METHODS: TrackingMethod[];
127
+
128
+ /**
129
+ * SDK 静态方法映射到 tracking 方法
130
+ */
131
+ export declare const SDK_TO_TRACKING_MAP: Record<string, string>;
132
+
133
+ // ============================================================
134
+ // 编译前检查器类型定义
135
+ // ============================================================
136
+
137
+ /**
138
+ * 检查结果
139
+ */
140
+ export interface CheckResult {
141
+ /** 是否通过 */
142
+ success: boolean;
143
+ /** 结果消息 */
144
+ message: string;
145
+ /** 额外数据 */
146
+ data?: any;
147
+ /** 错误对象(如果检查出错) */
148
+ error?: Error;
149
+ }
150
+
151
+ /**
152
+ * 检查器定义
153
+ */
154
+ export interface Checker {
155
+ /** 检查器名称 */
156
+ name: string;
157
+ /** 检查器描述 */
158
+ description: string;
159
+ /** 是否启用 */
160
+ enabled: boolean;
161
+ /** 验证函数 */
162
+ validator: (projectRoot: string, options: any) => Promise<CheckResult> | CheckResult;
163
+ }
164
+
165
+ /**
166
+ * 编译前检查选项
167
+ */
168
+ export interface PreBuildCheckOptions {
169
+ /** 只执行指定的检查器 ID */
170
+ only?: string[];
171
+ /** 跳过指定的检查器 ID */
172
+ skip?: string[];
173
+ /** 详细输出 */
174
+ verbose?: boolean;
175
+ /** 显示详细报告 */
176
+ showDetails?: boolean;
177
+ /** 遇到错误立即停止 */
178
+ failFast?: boolean;
179
+ /** 文件扩展名(传递给检查器) */
180
+ extensions?: string[];
181
+ /** 排除的目录(传递给检查器) */
182
+ excludeDirs?: string[];
183
+ }
184
+
185
+ /**
186
+ * 编译前检查结果
187
+ */
188
+ export interface PreBuildCheckResult {
189
+ /** 是否全部通过 */
190
+ success: boolean;
191
+ /** 各检查器的结果 */
192
+ results: Map<string, CheckResult>;
193
+ }
194
+
195
+ /**
196
+ * 执行所有已启用的编译前检查器
197
+ * @param projectRoot - 项目根目录
198
+ * @param options - 选项
199
+ */
200
+ export declare function runPreBuildChecks(
201
+ projectRoot: string,
202
+ options?: PreBuildCheckOptions
203
+ ): Promise<PreBuildCheckResult>;
204
+
205
+ /**
206
+ * 单独运行埋点验证(快捷方法)
207
+ * @param projectRoot - 项目根目录
208
+ * @param options - 选项
209
+ */
210
+ export declare function runTrackingCheck(
211
+ projectRoot: string,
212
+ options?: ValidationOptions
213
+ ): Promise<CheckResult>;
214
+
215
+ /**
216
+ * 注册自定义检查器
217
+ * @param id - 检查器 ID
218
+ * @param checker - 检查器配置
219
+ */
220
+ export declare function registerChecker(id: string, checker: Checker): void;
221
+
222
+ /**
223
+ * 启用/禁用检查器
224
+ * @param id - 检查器 ID
225
+ * @param enabled - 是否启用
226
+ */
227
+ export declare function toggleChecker(id: string, enabled: boolean): void;
228
+
229
+ /**
230
+ * 获取所有检查器列表
231
+ */
232
+ export declare function listCheckers(): Array<{
233
+ id: string;
234
+ name: string;
235
+ description: string;
236
+ enabled: boolean;
237
+ }>;
238
+
239
+ /**
240
+ * 内部检查器注册表(高级用户使用)
241
+ */
242
+ export declare const checkers: Map<string, Checker>;
@@ -0,0 +1,89 @@
1
+ "use strict";const{validateTracking,printValidationResult}=require("./tracking-validator.js");// ============================================================
2
+ // 检查器注册表
3
+ // ============================================================
4
+ /**
5
+ * 检查器定义
6
+ * @typedef {Object} Checker
7
+ * @property {string} name - 检查器名称
8
+ * @property {string} description - 检查器描述
9
+ * @property {boolean} enabled - 是否启用
10
+ * @property {(projectRoot: string, options: any) => Promise<CheckResult>|CheckResult} validator - 验证函数
11
+ *//**
12
+ * 检查结果
13
+ * @typedef {Object} CheckResult
14
+ * @property {boolean} success - 是否通过
15
+ * @property {string} message - 结果消息
16
+ * @property {any} [data] - 额外数据
17
+ *//**
18
+ * 所有注册的检查器
19
+ * @type {Map<string, Checker>}
20
+ */const checkers=new Map;// ============================================================
21
+ // 内置检查器
22
+ // ============================================================
23
+ /**
24
+ * 埋点验证检查器
25
+ */checkers.set("tracking",{name:"\u57CB\u70B9\u9A8C\u8BC1",description:"\u9A8C\u8BC1\u9879\u76EE\u4E2D\u662F\u5426\u6B63\u786E\u8C03\u7528\u4E86\u6240\u6709\u5FC5\u987B\u7684\u57CB\u70B9\u65B9\u6CD5",enabled:true,validator:(projectRoot,options={})=>{const result=validateTracking(projectRoot,{verbose:options.verbose||false,extensions:options.extensions,excludeDirs:options.excludeDirs});return{success:result.success,message:result.success?"\u2705 \u57CB\u70B9\u9A8C\u8BC1\u901A\u8FC7":`❌ 埋点验证失败:缺少 ${result.errors.length} 个必须的埋点方法`,data:result}}});// 预留:未来可添加更多检查器
26
+ // checkers.set('assets', {
27
+ // name: '资源检查',
28
+ // description: '验证项目资源文件的完整性和规范性',
29
+ // enabled: false,
30
+ // validator: (projectRoot, options) => {
31
+ // // TODO: 实现资源检查逻辑
32
+ // return { success: true, message: '资源检查通过' };
33
+ // }
34
+ // });
35
+ // checkers.set('config', {
36
+ // name: '配置检查',
37
+ // description: '验证项目配置文件的正确性',
38
+ // enabled: false,
39
+ // validator: (projectRoot, options) => {
40
+ // // TODO: 实现配置检查逻辑
41
+ // return { success: true, message: '配置检查通过' };
42
+ // }
43
+ // });
44
+ // ============================================================
45
+ // 检查器管理
46
+ // ============================================================
47
+ /**
48
+ * 注册自定义检查器
49
+ * @param {string} id - 检查器 ID
50
+ * @param {Checker} checker - 检查器配置
51
+ */function registerChecker(id,checker){if(checkers.has(id)){console.warn(`[警告] 检查器 "${id}" 已存在,将被覆盖`)}checkers.set(id,checker)}/**
52
+ * 启用/禁用检查器
53
+ * @param {string} id - 检查器 ID
54
+ * @param {boolean} enabled - 是否启用
55
+ */function toggleChecker(id,enabled){const checker=checkers.get(id);if(checker){checker.enabled=enabled}else{console.warn(`[警告] 未找到检查器: ${id}`)}}/**
56
+ * 获取所有检查器列表
57
+ * @returns {Array<{id: string, name: string, description: string, enabled: boolean}>}
58
+ */function listCheckers(){return Array.from(checkers.entries()).map(([id,checker])=>({id,name:checker.name,description:checker.description,enabled:checker.enabled}))}// ============================================================
59
+ // 执行检查
60
+ // ============================================================
61
+ /**
62
+ * 执行所有已启用的检查器
63
+ * @param {string} projectRoot - 项目根目录
64
+ * @param {Object} [options] - 选项
65
+ * @param {string[]} [options.only] - 只执行指定的检查器 ID
66
+ * @param {string[]} [options.skip] - 跳过指定的检查器 ID
67
+ * @param {boolean} [options.verbose] - 详细输出
68
+ * @param {boolean} [options.showDetails] - 显示详细报告
69
+ * @param {boolean} [options.failFast] - 遇到错误立即停止
70
+ * @returns {Promise<{success: boolean, results: Map<string, CheckResult>}>}
71
+ */async function runPreBuildChecks(projectRoot,options={}){const{only=[],skip=[],verbose=false,showDetails=true,failFast=false}=options;const results=new Map;let allSuccess=true;console.log("\n"+"\u2550".repeat(70));console.log("\uD83D\uDD0D \u7F16\u8BD1\u524D\u68C0\u67E5");console.log("\u2550".repeat(70));// 确定要执行的检查器
72
+ let checkersToRun=Array.from(checkers.entries()).filter(([id,checker])=>{// 如果指定了 only,只运行指定的
73
+ if(only.length>0&&!only.includes(id))return false;// 跳过 skip 列表中的
74
+ if(skip.includes(id))return false;// 只运行启用的
75
+ return checker.enabled});if(checkersToRun.length===0){console.log("\n\u26A0\uFE0F \u6CA1\u6709\u542F\u7528\u7684\u68C0\u67E5\u5668");console.log("\u2550".repeat(70)+"\n");return{success:true,results}}if(verbose){console.log(`\n📋 将执行 ${checkersToRun.length} 个检查器:`);checkersToRun.forEach(([id,checker])=>{console.log(` - ${checker.name} (${id})`)});console.log("")}// 执行检查器
76
+ for(const[id,checker]of checkersToRun){try{console.log(`\n▶️ 执行检查: ${checker.name}`);console.log("-".repeat(70));const result=await Promise.resolve(checker.validator(projectRoot,options));results.set(id,result);console.log(result.message);// 详细输出(针对埋点验证)
77
+ if(showDetails&&id==="tracking"&&result.data){printValidationResult(result.data,showDetails)}if(!result.success){allSuccess=false;if(failFast){console.log("\n\u26A0\uFE0F failFast \u6A21\u5F0F\uFF1A\u68C0\u6D4B\u5230\u5931\u8D25\uFF0C\u7EC8\u6B62\u540E\u7EED\u68C0\u67E5");break}}}catch(error){console.error(`\n❌ 检查器 "${checker.name}" 执行失败:`,error.message);results.set(id,{success:false,message:`执行出错: ${error.message}`,error});allSuccess=false;if(failFast)break}}// 总结
78
+ console.log("\n"+"\u2550".repeat(70));console.log("\uD83D\uDCCA \u68C0\u67E5\u603B\u7ED3");console.log("\u2550".repeat(70));const successCount=Array.from(results.values()).filter(r=>r.success).length;const totalCount=results.size;console.log(`\n通过: ${successCount}/${totalCount}`);if(allSuccess){console.log("\n\u2705 \u6240\u6709\u68C0\u67E5\u901A\u8FC7\uFF0C\u53EF\u4EE5\u7EE7\u7EED\u7F16\u8BD1\uFF01")}else{console.log("\n\u274C \u90E8\u5206\u68C0\u67E5\u5931\u8D25\uFF0C\u8BF7\u4FEE\u590D\u540E\u518D\u7F16\u8BD1\u3002")}console.log("\u2550".repeat(70)+"\n");return{success:allSuccess,results}}/**
79
+ * 单独运行埋点验证(快捷方法)
80
+ * @param {string} projectRoot - 项目根目录
81
+ * @param {Object} [options] - 选项
82
+ * @returns {Promise<CheckResult>}
83
+ */async function runTrackingCheck(projectRoot,options={}){const checker=checkers.get("tracking");if(!checker){throw new Error("\u57CB\u70B9\u68C0\u67E5\u5668\u672A\u6CE8\u518C")}return await Promise.resolve(checker.validator(projectRoot,options))}// ============================================================
84
+ // 导出
85
+ // ============================================================
86
+ module.exports={// 主要函数
87
+ runPreBuildChecks,runTrackingCheck,// 检查器管理
88
+ registerChecker,toggleChecker,listCheckers,// 内部访问(供高级用户)
89
+ checkers};