@playcraft/devkit 1.0.1-8.beta.1 → 1.0.1-8.beta.3

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 (206) hide show
  1. package/README.md +259 -0
  2. package/cli/bin/playable-scripts.js +44 -1
  3. package/cli/commands/build.js +130 -18
  4. package/cli/commands/builds.js +13 -2
  5. package/cli/commands/dev.js +7 -1
  6. package/cli/commands/pack.js +308 -39
  7. package/cli/commands/vite-dev.js +7 -1
  8. package/core/batch-build.js +1091 -125
  9. package/core/cos-uploader.js +264 -20
  10. package/core/index.js +71 -6
  11. package/core/loaders/gltf-loader.js +74 -28
  12. package/core/options.js +310 -15
  13. package/core/plugins/AdikteevInjectorPlugin.js +26 -4
  14. package/core/plugins/BigoAdsInjectorPlugin.js +21 -4
  15. package/core/plugins/DAPIInjectorPlugin.js +25 -2
  16. package/core/plugins/DebuggerInjectionPlugin.js +31 -3
  17. package/core/plugins/ExitAPIInjectorPlugin.js +57 -3
  18. package/core/plugins/FflateCompressionPlugin.js +225 -37
  19. package/core/plugins/FontInjectionWebpackPlugin.js +46 -4
  20. package/core/plugins/LiftoffInjectorPlugin.js +26 -4
  21. package/core/plugins/MRAIDInjectorPlugin.js +24 -2
  22. package/core/plugins/MintegralInjectorPlugin.js +25 -2
  23. package/core/plugins/PangleInjectorPlugin.js +24 -2
  24. package/core/plugins/SnapchatInjectorPlugin.js +26 -4
  25. package/core/plugins/TikTokInjectorPlugin.js +24 -2
  26. package/core/plugins/UnityInjectorPlugin.js +37 -8
  27. package/core/plugins/ZipPlugin.js +138 -19
  28. package/core/resources/fonts/maoken-regular.woff2 +0 -0
  29. package/core/resources/fonts/maoken-regular.woff2.chars +1 -0
  30. package/core/resources/fonts/maoken-zhuyuan.ttf +0 -0
  31. package/core/resources/fonts/pangmen-biaoti.ttf +0 -0
  32. package/core/resources/fonts/pangmen-regular.woff2 +0 -0
  33. package/core/resources/fonts/pangmen-regular.woff2.chars +1 -0
  34. package/core/utils/buildDefines.js +29 -2
  35. package/core/utils/buildTemplateString.js +40 -5
  36. package/core/utils/date.js +16 -1
  37. package/core/utils/fontRegistry.js +81 -12
  38. package/core/utils/fontResolver.js +335 -34
  39. package/core/utils/generateAdikteevHtmlWebpackPluginConfig.js +42 -7
  40. package/core/utils/generateBigabidHtmlWebpackPluginConfig.js +30 -3
  41. package/core/utils/generateInMobiHtmlWebpackPluginConfig.js +43 -7
  42. package/core/utils/injectSDKPlugins.js +58 -11
  43. package/core/utils/logOptions.js +37 -4
  44. package/core/utils/mergeOptions.js +19 -2
  45. package/core/utils/parseArgvOptions.js +110 -5
  46. package/core/utils/resolveChannelFold2Zip.js +15 -3
  47. package/core/utils/validateThemeData.js +220 -25
  48. package/core/validators/pre-build-checker.js +201 -21
  49. package/core/validators/tracking-validator.js +358 -40
  50. package/core/vite.dev.js +332 -18
  51. package/core/webpack.build.js +489 -56
  52. package/core/webpack.common.js +177 -11
  53. package/core/webpack.dev.js +82 -5
  54. package/package.json +3 -2
  55. package/core/resources/fonts/maoken/04e60956bbe055746aa7bca5c229ca8c.woff2 +0 -0
  56. package/core/resources/fonts/maoken/086a30eeaa0f5558062389361dca8685.woff2 +0 -0
  57. package/core/resources/fonts/maoken/1b1c9545aa1da01979d240f38f979b9e.woff2 +0 -0
  58. package/core/resources/fonts/maoken/1dd31fae60d98fcad0812cb4dc54afdd.woff2 +0 -0
  59. package/core/resources/fonts/maoken/1f08966635ca0dc4a64f519863df0dcc.woff2 +0 -0
  60. package/core/resources/fonts/maoken/26aab6a388b04c37fd5a4d066a512e14.woff2 +0 -0
  61. package/core/resources/fonts/maoken/29390db71394eb0d298fa1c339590e45.woff2 +0 -0
  62. package/core/resources/fonts/maoken/2b743d9f736c5cda9250fc8b85f69db8.woff2 +0 -0
  63. package/core/resources/fonts/maoken/2f16b1f8881909c4e65404610460797f.woff2 +0 -0
  64. package/core/resources/fonts/maoken/339dc88f6fd1b09336410154252f858f.woff2 +0 -0
  65. package/core/resources/fonts/maoken/3665591d2bd9a3f31a302a34a76c74dc.woff2 +0 -0
  66. package/core/resources/fonts/maoken/3b58ece5ff16a5addc9adc1c74717d12.woff2 +0 -0
  67. package/core/resources/fonts/maoken/3e3aacd6cd2255be39c97c4ed6eb978b.woff2 +0 -0
  68. package/core/resources/fonts/maoken/3e8cdd750fcbbfcf9a1101c7418eeb36.woff2 +0 -0
  69. package/core/resources/fonts/maoken/3f71b3c0ecb555f9fa88d1b2ae0ec77f.woff2 +0 -0
  70. package/core/resources/fonts/maoken/40f956d892d89a442992737448f0330b.woff2 +0 -0
  71. package/core/resources/fonts/maoken/47ecbb41501b6cb923aaa8b8324bb22f.woff2 +0 -0
  72. package/core/resources/fonts/maoken/4a6e704952e05d217e4f4d2c2a330094.woff2 +0 -0
  73. package/core/resources/fonts/maoken/4ec9fc653a6aaabd2296fac3fe906416.woff2 +0 -0
  74. package/core/resources/fonts/maoken/5d950afcbaac4bb8f44083e5f4572957.woff2 +0 -0
  75. package/core/resources/fonts/maoken/6505b718e31b5b2b29c4f50a2a2b5fe6.woff2 +0 -0
  76. package/core/resources/fonts/maoken/673405ebfdfebae1104f36efa778714d.woff2 +0 -0
  77. package/core/resources/fonts/maoken/695a686e27152d8e81b4459b64d7b6a1.woff2 +0 -0
  78. package/core/resources/fonts/maoken/6b5fb0c960eed7a53f39adb18d0ab28b.woff2 +0 -0
  79. package/core/resources/fonts/maoken/6f53ca6c761ff540e11d5f7111dad67c.woff2 +0 -0
  80. package/core/resources/fonts/maoken/7125e9df28437d053e56f85a908a7343.woff2 +0 -0
  81. package/core/resources/fonts/maoken/731865983ae51e2a4bdc6c194da0b82e.woff2 +0 -0
  82. package/core/resources/fonts/maoken/74d993e9aa68ef636eb1287439d06bb0.woff2 +0 -0
  83. package/core/resources/fonts/maoken/797653f0a72360cd584f625972b5d217.woff2 +0 -0
  84. package/core/resources/fonts/maoken/7af5930fe5e6dade3cf31d3287d6414c.woff2 +0 -0
  85. package/core/resources/fonts/maoken/7c6fe2ee55f924f00b097da208852a34.woff2 +0 -0
  86. package/core/resources/fonts/maoken/8cff8ef36a011ca7e6d103f29b22f98b.woff2 +0 -0
  87. package/core/resources/fonts/maoken/8ff8566257c5df17ddde67b2b48243a4.woff2 +0 -0
  88. package/core/resources/fonts/maoken/93988344f6df526a0033a05d2d927d76.woff2 +0 -0
  89. package/core/resources/fonts/maoken/99d1c5647f1ce540674bd32354c2038f.woff2 +0 -0
  90. package/core/resources/fonts/maoken/a328b132aac2a3f53d97b8e7ad940e1a.woff2 +0 -0
  91. package/core/resources/fonts/maoken/a7dc00c95dd1e7a8eca8053975b0c07c.woff2 +0 -0
  92. package/core/resources/fonts/maoken/ab93ca86fb3e7ad8e95b93f7de5d8f97.woff2 +0 -0
  93. package/core/resources/fonts/maoken/b1b83e931f7e2187786b87545a6ee842.woff2 +0 -0
  94. package/core/resources/fonts/maoken/bb31b8c0ccf2d93af7cc25f19a576970.woff2 +0 -0
  95. package/core/resources/fonts/maoken/bd4a61534e4a0f426dd0d396362b211d.woff2 +0 -0
  96. package/core/resources/fonts/maoken/bec7534be141c6473023f18f5aee7a0e.woff2 +0 -0
  97. package/core/resources/fonts/maoken/c133a76c68c645b46b20e7faf9894ab6.woff2 +0 -0
  98. package/core/resources/fonts/maoken/c2deab25bd23b63c794da537e208c61a.woff2 +0 -0
  99. package/core/resources/fonts/maoken/cf13611d9749dd23fd9737073d2110f6.woff2 +0 -0
  100. package/core/resources/fonts/maoken/d01182e5dd9f51dce713916e8f872bbf.woff2 +0 -0
  101. package/core/resources/fonts/maoken/d1d4c88b2a14e679d3cc9d0ad8e8abdb.woff2 +0 -0
  102. package/core/resources/fonts/maoken/df61c7f5fa45aa6156122c09653f6700.woff2 +0 -0
  103. package/core/resources/fonts/maoken/e1867d1454d8c903ba29bac70f88c211.woff2 +0 -0
  104. package/core/resources/fonts/maoken/ef912f41f9109c5d936005af5552f521.woff2 +0 -0
  105. package/core/resources/fonts/maoken/f0f119744871bb6bec7e0eef4e1ce8ba.woff2 +0 -0
  106. package/core/resources/fonts/maoken/f32a268245b50f6c19bd946239fd8cc0.woff2 +0 -0
  107. package/core/resources/fonts/maoken/f4735851e08da122f471100ecff5c4ae.woff2 +0 -0
  108. package/core/resources/fonts/maoken/f85b8f6994600622f767506ae55d6280.woff2 +0 -0
  109. package/core/resources/fonts/maoken/fc145bdf9c20b67db44156382155b465.woff2 +0 -0
  110. package/core/resources/fonts/maoken/fcd8571ec88b953c893d77c4fd5c6ebc.woff2 +0 -0
  111. package/core/resources/fonts/maoken/fcdcb3f4b26b5c1f45fdd151f45e0c6d.woff2 +0 -0
  112. package/core/resources/fonts/maoken/index.json +0 -59
  113. package/core/resources/fonts/pangmen/0047cd3053f8c06117a653b73e100391.woff2 +0 -0
  114. package/core/resources/fonts/pangmen/008870151036457b39ee8794f5bc7a64.woff2 +0 -0
  115. package/core/resources/fonts/pangmen/01b5355e45274055ad794cc1b1305e69.woff2 +0 -0
  116. package/core/resources/fonts/pangmen/0756361ea2445ec1c500d354fa0867d0.woff2 +0 -0
  117. package/core/resources/fonts/pangmen/091cf1201fbbda8dc98c01e775991bab.woff2 +0 -0
  118. package/core/resources/fonts/pangmen/0c854072dc9998bd02ea8b29b5f1f55e.woff2 +0 -0
  119. package/core/resources/fonts/pangmen/10f3f7f8da6ab5f882e816c97532a4c6.woff2 +0 -0
  120. package/core/resources/fonts/pangmen/141229a7039c633c894ea5168d0ae849.woff2 +0 -0
  121. package/core/resources/fonts/pangmen/1b0b982276f5f93b3c71b3f6b1b81184.woff2 +0 -0
  122. package/core/resources/fonts/pangmen/1e415e51c4efb3182b87665d726fc423.woff2 +0 -0
  123. package/core/resources/fonts/pangmen/1f5fb0c8d785813dc765ff889b491361.woff2 +0 -0
  124. package/core/resources/fonts/pangmen/1fe8065222947d67d4177fe3bdd782cd.woff2 +0 -0
  125. package/core/resources/fonts/pangmen/229674de0b8d208f607468537d10ca48.woff2 +0 -0
  126. package/core/resources/fonts/pangmen/27230f128a3708090bed2eb5bb6ad437.woff2 +0 -0
  127. package/core/resources/fonts/pangmen/2d8f78526cd5d63e22274b3f0e5c4e50.woff2 +0 -0
  128. package/core/resources/fonts/pangmen/2f441859584ca1d060378be9f1173a6a.woff2 +0 -0
  129. package/core/resources/fonts/pangmen/319ed7be5128268f3cf297464b305a55.woff2 +0 -0
  130. package/core/resources/fonts/pangmen/35a41344eec91ef694ca59fda4dc9ccc.woff2 +0 -0
  131. package/core/resources/fonts/pangmen/3715cbec78511ff67e44fdd363b5fd79.woff2 +0 -0
  132. package/core/resources/fonts/pangmen/37d468e8e898644f8fddc9f83f04a5b1.woff2 +0 -0
  133. package/core/resources/fonts/pangmen/384b560d66242e5c1062489020d8749c.woff2 +0 -0
  134. package/core/resources/fonts/pangmen/38ef10dd61436ebaeb8dada7b827595c.woff2 +0 -0
  135. package/core/resources/fonts/pangmen/3e8bdfd8597f723100ff7d6b54e803f9.woff2 +0 -0
  136. package/core/resources/fonts/pangmen/43d57e12e1ec85c68a289d88f920f62e.woff2 +0 -0
  137. package/core/resources/fonts/pangmen/4c4ee1006529ddcc67e7c195d5ddb2e0.woff2 +0 -0
  138. package/core/resources/fonts/pangmen/4f65210dd5014352f4c6eb1a791e56c9.woff2 +0 -0
  139. package/core/resources/fonts/pangmen/5179f5b92b71d655b9094b282ca39161.woff2 +0 -0
  140. package/core/resources/fonts/pangmen/5282fc1cc80a878d744621a36279a1fa.woff2 +0 -0
  141. package/core/resources/fonts/pangmen/57604d7bad494a118e0fa9a6c0f1035a.woff2 +0 -0
  142. package/core/resources/fonts/pangmen/5891eaedeb9999f6b71434efc42407db.woff2 +0 -0
  143. package/core/resources/fonts/pangmen/592e3b1817378c607c8b6b183c89019a.woff2 +0 -0
  144. package/core/resources/fonts/pangmen/5c7c16778cf226e25ef39e475a19afa0.woff2 +0 -0
  145. package/core/resources/fonts/pangmen/5eccbf1ee87f73db786ee0a8441df8a3.woff2 +0 -0
  146. package/core/resources/fonts/pangmen/606a4091be7a3307a2d9adc4d2206b04.woff2 +0 -0
  147. package/core/resources/fonts/pangmen/62da615f3cbb7d19ce02973b009ec5b9.woff2 +0 -0
  148. package/core/resources/fonts/pangmen/63f9972982226af9df16006408160e8a.woff2 +0 -0
  149. package/core/resources/fonts/pangmen/6702bbfff577fce5bc621a91fdcb78bc.woff2 +0 -0
  150. package/core/resources/fonts/pangmen/6cd32dbf440a9b7711cc293b19f4c2d7.woff2 +0 -0
  151. package/core/resources/fonts/pangmen/6ef49a0811c0a0892dfbca6602365e6f.woff2 +0 -0
  152. package/core/resources/fonts/pangmen/7194cb1fa135d58fee1611bb8915fe25.woff2 +0 -0
  153. package/core/resources/fonts/pangmen/71baf3daab3248b0748dd187ab2fbc45.woff2 +0 -0
  154. package/core/resources/fonts/pangmen/7457db009eff249ec587c7d912c5d347.woff2 +0 -0
  155. package/core/resources/fonts/pangmen/762d1c7a28837c08180123e98dbe8cbf.woff2 +0 -0
  156. package/core/resources/fonts/pangmen/76e3d855d8684a1ee4d12cf7a572ca78.woff2 +0 -0
  157. package/core/resources/fonts/pangmen/78a97efdf675a6dcf903b4725de01219.woff2 +0 -0
  158. package/core/resources/fonts/pangmen/7b42a8cbf34bd2a0820ed81bb4730b6d.woff2 +0 -0
  159. package/core/resources/fonts/pangmen/7d07b13e8fc6502f0d2e21a7bb87ddb1.woff2 +0 -0
  160. package/core/resources/fonts/pangmen/7e710c007a48e4410861c67a9a26d4fc.woff2 +0 -0
  161. package/core/resources/fonts/pangmen/7eb8fe8685f6f2d6f0e223337f3a2b2b.woff2 +0 -0
  162. package/core/resources/fonts/pangmen/7ec1b00b541c2c2817cf352e2634bf5b.woff2 +0 -0
  163. package/core/resources/fonts/pangmen/7ffdd7059669ad52af7995b515889eeb.woff2 +0 -0
  164. package/core/resources/fonts/pangmen/818c6b2f12623b5a43d1036cb7f71b61.woff2 +0 -0
  165. package/core/resources/fonts/pangmen/81983731abfefd6a400b83b93d9e3665.woff2 +0 -0
  166. package/core/resources/fonts/pangmen/821ad173f8fd8eccd214241cc651657b.woff2 +0 -0
  167. package/core/resources/fonts/pangmen/83ee6dce60a5f156da3ad18e5cfb3e6e.woff2 +0 -0
  168. package/core/resources/fonts/pangmen/87db73da676a2e7b3e640e84187a5c86.woff2 +0 -0
  169. package/core/resources/fonts/pangmen/89b2edc17dd7ab4332f18b038d268068.woff2 +0 -0
  170. package/core/resources/fonts/pangmen/8b3f89232252b80f6e12ab91d93fda96.woff2 +0 -0
  171. package/core/resources/fonts/pangmen/928a7b39a7c4efdca10f70dfcdb917ac.woff2 +0 -0
  172. package/core/resources/fonts/pangmen/945d03601ca6e8be5f7e6043ecb716a5.woff2 +0 -0
  173. package/core/resources/fonts/pangmen/97de16fa122ebcbff22956b7b2de2f77.woff2 +0 -0
  174. package/core/resources/fonts/pangmen/9882677dd482462a6c1c0a1d7efbb15d.woff2 +0 -0
  175. package/core/resources/fonts/pangmen/9c46188cc1e1711f860fa06d2c29dcc1.woff2 +0 -0
  176. package/core/resources/fonts/pangmen/a279650fd2f2f115ab30cbbf748f85f9.woff2 +0 -0
  177. package/core/resources/fonts/pangmen/a3bfd69906b38ab891501ac997bd577d.woff2 +0 -0
  178. package/core/resources/fonts/pangmen/a4cc4f73c30d1dca3f7abd656705e239.woff2 +0 -0
  179. package/core/resources/fonts/pangmen/a54d94633b077f678d15b96ad9e6a9c9.woff2 +0 -0
  180. package/core/resources/fonts/pangmen/ab9bd513cba626dee079d3f8f35da118.woff2 +0 -0
  181. package/core/resources/fonts/pangmen/b4b057c12d1e44681bdb5c5e92f8e070.woff2 +0 -0
  182. package/core/resources/fonts/pangmen/b8c1e4b1ab3beb334fa773a50c9221b0.woff2 +0 -0
  183. package/core/resources/fonts/pangmen/b93617eda1294f0696193c5237320892.woff2 +0 -0
  184. package/core/resources/fonts/pangmen/bac50a33e26632667ed330c817b37dcf.woff2 +0 -0
  185. package/core/resources/fonts/pangmen/c31e6efe815ccefa90bebc1fa64aba8b.woff2 +0 -0
  186. package/core/resources/fonts/pangmen/c75ff5aa44a05db009b0a38d05f4040b.woff2 +0 -0
  187. package/core/resources/fonts/pangmen/ca70255a9a388b3f33cff49d5abc31f2.woff2 +0 -0
  188. package/core/resources/fonts/pangmen/cb249c8c31a2bee3d6a68fd1c74c061a.woff2 +0 -0
  189. package/core/resources/fonts/pangmen/cb898d32532efe5a7933672228bc620c.woff2 +0 -0
  190. package/core/resources/fonts/pangmen/cc3c33c20e8abd03b9e5acc637985d63.woff2 +0 -0
  191. package/core/resources/fonts/pangmen/cca20bec40d1a11ede1ec496d22cd026.woff2 +0 -0
  192. package/core/resources/fonts/pangmen/cedee24be6ea34e35c53c0ce5edd7af0.woff2 +0 -0
  193. package/core/resources/fonts/pangmen/d42c580643039640037f5c63750ac711.woff2 +0 -0
  194. package/core/resources/fonts/pangmen/dbe1c94b8d92f8915c33fff55b625716.woff2 +0 -0
  195. package/core/resources/fonts/pangmen/dced2d67eceeefa7f45ba44854174b8b.woff2 +0 -0
  196. package/core/resources/fonts/pangmen/dee98b9b0545a42fd3aaa87ec1fa213e.woff2 +0 -0
  197. package/core/resources/fonts/pangmen/e6ac6bb1654d431fe62556a342b16e52.woff2 +0 -0
  198. package/core/resources/fonts/pangmen/eb9be6e3e450bf5706308fcec576c6df.woff2 +0 -0
  199. package/core/resources/fonts/pangmen/ee8cb027810ba5b5d9016774789f747b.woff2 +0 -0
  200. package/core/resources/fonts/pangmen/f0ecb29938306af8e9117a24eefad319.woff2 +0 -0
  201. package/core/resources/fonts/pangmen/f1a1a697d45d7f13a71a0a38424a98a7.woff2 +0 -0
  202. package/core/resources/fonts/pangmen/f203894451810a009d6795f1b0493989.woff2 +0 -0
  203. package/core/resources/fonts/pangmen/f53b808c0d5afb527499049ba225198e.woff2 +0 -0
  204. package/core/resources/fonts/pangmen/f6f69823eda331486d1d1a6e676e1d4a.woff2 +0 -0
  205. package/core/resources/fonts/pangmen/fb1ad0e1ba9ca84b66aa88867ce4109c.woff2 +0 -0
  206. package/core/resources/fonts/pangmen/index.json +0 -95
@@ -1,6 +1,10 @@
1
- "use strict";const fs=require("fs");const path=require("path");// ============================================================
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ // ============================================================
2
5
  // 埋点方法配置
3
6
  // ============================================================
7
+
4
8
  /**
5
9
  * 埋点方法配置
6
10
  * @typedef {Object} TrackingMethod
@@ -8,30 +12,150 @@
8
12
  * @property {boolean} required - 是否必须调用
9
13
  * @property {string} description - 方法描述
10
14
  * @property {string[]} channels - 对应的渠道埋点
11
- *//**
15
+ */
16
+
17
+ /**
12
18
  * 所有埋点方法配置
13
19
  * @type {TrackingMethod[]}
14
- */const TRACKING_METHODS=[{name:"onPlaycraftTrackingInit",required:true,description:"SDK \u521D\u59CB\u5316\u5F00\u59CB",channels:["Bigabid:mraid_viewable","InMobi:Ad_Load_Start","AppLovin:LOADING"]},{name:"onPlaycraftTrackingLoading",required:true,description:"\u6E38\u620F\u8D44\u6E90\u52A0\u8F7D\u4E2D",channels:["Bigabid:game_viewable","InMobi:Ad_Viewable","AppLovin:LOADED"]},{name:"onPlaycraftTrackingLoaded",required:true,description:"\u4E3B\u573A\u666F\u5C31\u7EEA\uFF0C\u7528\u6237\u53EF\u4EA4\u4E92",channels:["AppLovin:DISPLAYED"]},{name:"onPlaycraftChallengeStart",required:true,description:"\u6311\u6218\u5F00\u59CB\uFF08\u9996\u6B21\u7528\u6237\u4EA4\u4E92\uFF09",channels:["Bigabid:engagement","InMobi:First_Engagement","AppLovin:CHALLENGE_STARTED"]},{name:"onPlaycraftChallengeProgress",required:false,description:"\u8FDB\u5EA6\u767E\u5206\u6BD4\u8FFD\u8E2A\uFF08\u53EF\u9009\uFF09",channels:["AppLovin:CHALLENGE_PASS_*"]},{name:"onPlaycraftChallengeSuccess",required:true,description:"\u6E38\u620F\u6311\u6218\u6210\u529F",channels:["AppLovin:CHALLENGE_SOLVED","Bigabid:complete","InMobi:Gameplay_Complete"]},{name:"onPlaycraftChallengeFinish",required:false,description:"\u6E38\u620F\u7ED3\u675F\uFF08\u53EF\u9009\uFF09",channels:["Bigabid:complete","InMobi:Gameplay_Complete","AppLovin:ENDCARD_SHOWN"]},{name:"onPlaycraftInstall",required:true,description:"\u70B9\u51FB\u5B89\u88C5",channels:["Bigabid:click","InMobi:DSP_Click","AppLovin:CTA_CLICKED"]},{name:"onPlaycraftRetry",required:false,description:"\u91CD\u8BD5\u6E38\u620F\uFF08\u53EF\u9009\uFF09",channels:["AppLovin:CHALLENGE_RETRY"]},{name:"onPlaycraftChallengeFailed",required:true,description:"\u6E38\u620F\u6311\u6218\u5931\u8D25",channels:["AppLovin:CHALLENGE_FAILED"]}];/**
20
+ */
21
+ const TRACKING_METHODS = [
22
+ {
23
+ name: 'onPlaycraftTrackingInit',
24
+ required: true,
25
+ description: 'SDK 初始化开始',
26
+ channels: ['Bigabid:mraid_viewable', 'InMobi:Ad_Load_Start', 'AppLovin:LOADING']
27
+ },
28
+ {
29
+ name: 'onPlaycraftTrackingLoading',
30
+ required: true,
31
+ description: '游戏资源加载中',
32
+ channels: ['Bigabid:game_viewable', 'InMobi:Ad_Viewable', 'AppLovin:LOADED']
33
+ },
34
+ {
35
+ name: 'onPlaycraftTrackingLoaded',
36
+ required: true,
37
+ description: '主场景就绪,用户可交互',
38
+ channels: ['AppLovin:DISPLAYED']
39
+ },
40
+ {
41
+ name: 'onPlaycraftChallengeStart',
42
+ required: true,
43
+ description: '挑战开始(首次用户交互)',
44
+ channels: ['Bigabid:engagement', 'InMobi:First_Engagement', 'AppLovin:CHALLENGE_STARTED']
45
+ },
46
+ {
47
+ name: 'onPlaycraftChallengeProgress',
48
+ required: false,
49
+ description: '进度百分比追踪(可选)',
50
+ channels: ['AppLovin:CHALLENGE_PASS_*']
51
+ },
52
+ {
53
+ name: 'onPlaycraftChallengeSuccess',
54
+ required: true,
55
+ description: '游戏挑战成功',
56
+ channels: ['AppLovin:CHALLENGE_SOLVED', 'Bigabid:complete', 'InMobi:Gameplay_Complete']
57
+ },
58
+ {
59
+ name: 'onPlaycraftChallengeFinish',
60
+ required: false,
61
+ description: '游戏结束(可选)',
62
+ channels: ['Bigabid:complete', 'InMobi:Gameplay_Complete', 'AppLovin:ENDCARD_SHOWN']
63
+ },
64
+ {
65
+ name: 'onPlaycraftInstall',
66
+ required: true,
67
+ description: '点击安装',
68
+ channels: ['Bigabid:click', 'InMobi:DSP_Click', 'AppLovin:CTA_CLICKED']
69
+ },
70
+ {
71
+ name: 'onPlaycraftRetry',
72
+ required: false,
73
+ description: '重试游戏(可选)',
74
+ channels: ['AppLovin:CHALLENGE_RETRY']
75
+ },
76
+ {
77
+ name: 'onPlaycraftChallengeFailed',
78
+ required: true,
79
+ description: '游戏挑战失败',
80
+ channels: ['AppLovin:CHALLENGE_FAILED']
81
+ }
82
+ ];
83
+
84
+ /**
15
85
  * SDK 静态方法映射到 tracking 方法
16
86
  * 这些 SDK 方法内部会调用对应的 tracking 方法
17
- */const SDK_TO_TRACKING_MAP={"playcraftInit":"onPlaycraftTrackingInit","playcraftStart":"onPlaycraftTrackingLoading","playcraftInteractive":"onPlaycraftTrackingLoaded","playcraftChallengeStart":"onPlaycraftChallengeStart","recordPlaycraftChallengeProgress":"onPlaycraftChallengeProgress","playcraftRecordSuccess":"onPlaycraftChallengeSuccess","playcraftFinish":"onPlaycraftChallengeFinish","playcraftInstall":"onPlaycraftInstall","playcraftRetry":"onPlaycraftRetry","playcraftRecordFailed":"onPlaycraftChallengeFailed"};// ============================================================
87
+ */
88
+ const SDK_TO_TRACKING_MAP = {
89
+ 'playcraftInit': 'onPlaycraftTrackingInit',
90
+ 'playcraftStart': 'onPlaycraftTrackingLoading',
91
+ 'playcraftInteractive': 'onPlaycraftTrackingLoaded',
92
+ 'playcraftChallengeStart': 'onPlaycraftChallengeStart',
93
+ 'recordPlaycraftChallengeProgress': 'onPlaycraftChallengeProgress',
94
+ 'playcraftRecordSuccess': 'onPlaycraftChallengeSuccess',
95
+ 'playcraftFinish': 'onPlaycraftChallengeFinish',
96
+ 'playcraftInstall': 'onPlaycraftInstall',
97
+ 'playcraftRetry': 'onPlaycraftRetry',
98
+ 'playcraftRecordFailed': 'onPlaycraftChallengeFailed'
99
+ };
100
+
101
+ // ============================================================
18
102
  // 文件扫描工具
19
103
  // ============================================================
104
+
20
105
  /**
21
106
  * 递归扫描目录获取所有源码文件
22
107
  * @param {string} dir - 目录路径
23
108
  * @param {string[]} extensions - 文件扩展名列表
24
109
  * @param {string[]} excludeDirs - 排除的目录名列表
25
110
  * @returns {string[]} 文件路径列表
26
- */function scanSourceFiles(dir,extensions=[".ts",".tsx",".js",".jsx"],excludeDirs=["node_modules","dist","build",".git",".claude"]){const files=[];function scan(currentDir){if(!fs.existsSync(currentDir))return;const entries=fs.readdirSync(currentDir,{withFileTypes:true});for(const entry of entries){const fullPath=path.join(currentDir,entry.name);if(entry.isDirectory()){// 跳过排除的目录
27
- if(excludeDirs.includes(entry.name))continue;scan(fullPath)}else if(entry.isFile()){// 检查文件扩展名
28
- const ext=path.extname(entry.name);if(extensions.includes(ext)){files.push(fullPath)}}}}scan(dir);return files}/**
111
+ */
112
+ function scanSourceFiles(dir, extensions = ['.ts', '.tsx', '.js', '.jsx'], excludeDirs = ['node_modules', 'dist', 'build', '.git', '.claude']) {
113
+ const files = [];
114
+
115
+ function scan(currentDir) {
116
+ if (!fs.existsSync(currentDir)) return;
117
+
118
+ const entries = fs.readdirSync(currentDir, { withFileTypes: true });
119
+
120
+ for (const entry of entries) {
121
+ const fullPath = path.join(currentDir, entry.name);
122
+
123
+ if (entry.isDirectory()) {
124
+ // 跳过排除的目录
125
+ if (excludeDirs.includes(entry.name)) continue;
126
+ scan(fullPath);
127
+ } else if (entry.isFile()) {
128
+ // 检查文件扩展名
129
+ const ext = path.extname(entry.name);
130
+ if (extensions.includes(ext)) {
131
+ files.push(fullPath);
132
+ }
133
+ }
134
+ }
135
+ }
136
+
137
+ scan(dir);
138
+ return files;
139
+ }
140
+
141
+ /**
29
142
  * 读取文件内容(带错误处理)
30
143
  * @param {string} filePath - 文件路径
31
144
  * @returns {string|null} 文件内容或 null
32
- */function readFileContent(filePath){try{return fs.readFileSync(filePath,"utf-8")}catch(err){console.warn(`[警告] 无法读取文件: ${filePath}`,err.message);return null}}// ============================================================
145
+ */
146
+ function readFileContent(filePath) {
147
+ try {
148
+ return fs.readFileSync(filePath, 'utf-8');
149
+ } catch (err) {
150
+ console.warn(`[警告] 无法读取文件: ${filePath}`, err.message);
151
+ return null;
152
+ }
153
+ }
154
+
155
+ // ============================================================
33
156
  // 方法调用检测
34
157
  // ============================================================
158
+
35
159
  /**
36
160
  * 检测代码中是否包含 tracking 方法的直接调用
37
161
  * @param {string} content - 文件内容
@@ -40,10 +164,21 @@ const ext=path.extname(entry.name);if(extensions.includes(ext)){files.push(fullP
40
164
  *
41
165
  * 注意:不依赖对象名称,只检测方法调用模式
42
166
  * 匹配:xxx.onPlaycraftXxx( 或 xxx['onPlaycraftXxx']( 或 xxx["onPlaycraftXxx"](
43
- */function detectDirectTrackingCall(content,methodName){const patterns=[// 匹配 .方法名(
44
- new RegExp(`\\.${methodName}\\s*\\(`),// 匹配 ['方法名'](
45
- new RegExp(`\\['${methodName}'\\]\\s*\\(`),// 匹配 ["方法名"](
46
- new RegExp(`\\["${methodName}"\\]\\s*\\(`)];return patterns.some(pattern=>pattern.test(content))}/**
167
+ */
168
+ function detectDirectTrackingCall(content, methodName) {
169
+ const patterns = [
170
+ // 匹配 .方法名(
171
+ new RegExp(`\\.${methodName}\\s*\\(`),
172
+ // 匹配 ['方法名'](
173
+ new RegExp(`\\['${methodName}'\\]\\s*\\(`),
174
+ // 匹配 ["方法名"](
175
+ new RegExp(`\\["${methodName}"\\]\\s*\\(`)
176
+ ];
177
+
178
+ return patterns.some(pattern => pattern.test(content));
179
+ }
180
+
181
+ /**
47
182
  * 检测代码中是否包含 SDK 方法调用(会间接触发 tracking)
48
183
  * @param {string} content - 文件内容
49
184
  * @param {string} sdkMethod - SDK 方法名
@@ -51,20 +186,44 @@ new RegExp(`\\["${methodName}"\\]\\s*\\(`)];return patterns.some(pattern=>patter
51
186
  *
52
187
  * 注意:不依赖对象名称,只检测方法调用模式
53
188
  * 匹配:xxx.playcraftXxx( 或 xxx['playcraftXxx']( 或 xxx["playcraftXxx"](
54
- */function detectSDKCall(content,sdkMethod){const patterns=[// 匹配 .方法名(
55
- new RegExp(`\\.${sdkMethod}\\s*\\(`),// 匹配 ['方法名'](
56
- new RegExp(`\\['${sdkMethod}'\\]\\s*\\(`),// 匹配 ["方法名"](
57
- new RegExp(`\\["${sdkMethod}"\\]\\s*\\(`)];return patterns.some(pattern=>pattern.test(content))}/**
189
+ */
190
+ function detectSDKCall(content, sdkMethod) {
191
+ const patterns = [
192
+ // 匹配 .方法名(
193
+ new RegExp(`\\.${sdkMethod}\\s*\\(`),
194
+ // 匹配 ['方法名'](
195
+ new RegExp(`\\['${sdkMethod}'\\]\\s*\\(`),
196
+ // 匹配 ["方法名"](
197
+ new RegExp(`\\["${sdkMethod}"\\]\\s*\\(`)
198
+ ];
199
+
200
+ return patterns.some(pattern => pattern.test(content));
201
+ }
202
+
203
+ /**
58
204
  * 检测是否导入了 @playcraft/ads-tracking
59
205
  * @param {string} content - 文件内容
60
206
  * @returns {boolean} 是否导入了 tracking
61
- */function hasTrackingImport(content){return /from\s+['"]@playcraft\/ads-tracking['"]/.test(content)||/require\s*\(\s*['"]@playcraft\/ads-tracking['"]\s*\)/.test(content)}/**
207
+ */
208
+ function hasTrackingImport(content) {
209
+ return /from\s+['"]@playcraft\/ads-tracking['"]/.test(content) ||
210
+ /require\s*\(\s*['"]@playcraft\/ads-tracking['"]\s*\)/.test(content);
211
+ }
212
+
213
+ /**
62
214
  * 检测是否导入了 @playcraft/adsdk
63
215
  * @param {string} content - 文件内容
64
216
  * @returns {boolean} 是否导入了 SDK
65
- */function hasSDKImport(content){return /from\s+['"]@playcraft\/adsdk['"]/.test(content)||/require\s*\(\s*['"]@playcraft\/adsdk['"]\s*\)/.test(content)}// ============================================================
217
+ */
218
+ function hasSDKImport(content) {
219
+ return /from\s+['"]@playcraft\/adsdk['"]/.test(content) ||
220
+ /require\s*\(\s*['"]@playcraft\/adsdk['"]\s*\)/.test(content);
221
+ }
222
+
223
+ // ============================================================
66
224
  // 验证核心逻辑
67
225
  // ============================================================
226
+
68
227
  /**
69
228
  * 验证结果
70
229
  * @typedef {Object} ValidationResult
@@ -74,7 +233,9 @@ new RegExp(`\\["${sdkMethod}"\\]\\s*\\(`)];return patterns.some(pattern=>pattern
74
233
  * @property {string[]} errors - 错误信息列表
75
234
  * @property {string[]} warnings - 警告信息列表
76
235
  * @property {Object} stats - 统计信息
77
- *//**
236
+ */
237
+
238
+ /**
78
239
  * 验证项目中的埋点调用
79
240
  * @param {string} projectRoot - 项目根目录
80
241
  * @param {Object} [options] - 验证选项
@@ -82,32 +243,189 @@ new RegExp(`\\["${sdkMethod}"\\]\\s*\\(`)];return patterns.some(pattern=>pattern
82
243
  * @param {string[]} [options.excludeDirs] - 排除的目录
83
244
  * @param {boolean} [options.verbose] - 是否输出详细信息
84
245
  * @returns {ValidationResult} 验证结果
85
- */function validateTracking(projectRoot,options={}){const{extensions=[".ts",".tsx",".js",".jsx"],excludeDirs=["node_modules","dist","build",".git"],verbose=false}=options;const result={success:false,integrationType:"none",methods:[],errors:[],warnings:[],stats:{totalFiles:0,scannedFiles:0,hasTrackingImport:false,hasSDKImport:false}};// 1. 扫描所有源码文件
86
- if(verbose)console.log(`\n[扫描] 开始扫描项目: ${projectRoot}`);const files=scanSourceFiles(projectRoot,extensions,excludeDirs);result.stats.totalFiles=files.length;if(files.length===0){result.errors.push("\u672A\u627E\u5230\u4EFB\u4F55\u6E90\u7801\u6587\u4EF6");return result}if(verbose)console.log(`[扫描] 找到 ${files.length} 个源码文件\n`);// 2. 读取所有文件内容并合并(用于全局检测)
87
- let allContent="";for(const file of files){const content=readFileContent(file);if(content){allContent+=content+"\n";result.stats.scannedFiles++}}// 3. 判断集成类型
88
- result.stats.hasTrackingImport=hasTrackingImport(allContent);result.stats.hasSDKImport=hasSDKImport(allContent);if(result.stats.hasTrackingImport&&result.stats.hasSDKImport){result.integrationType="sdk";// SDK 优先
89
- }else if(result.stats.hasSDKImport){result.integrationType="sdk"}else if(result.stats.hasTrackingImport){result.integrationType="tracking"}else{result.errors.push("\u672A\u68C0\u6D4B\u5230 @playcraft/adsdk \u6216 @playcraft/ads-tracking \u7684\u5BFC\u5165");return result}if(verbose){console.log(`[集成类型] ${result.integrationType==="sdk"?"SDK \u96C6\u6210\uFF08\u901A\u8FC7 sdk \u9759\u6001\u65B9\u6CD5\u8C03\u7528\uFF09":"\u76F4\u63A5 tracking \u96C6\u6210"}`);console.log(`[导入检测] SDK: ${result.stats.hasSDKImport?"\u2713":"\u2717"} | Tracking: ${result.stats.hasTrackingImport?"\u2713":"\u2717"}\n`)}// 4. 检测每个埋点方法
90
- for(const method of TRACKING_METHODS){let found=false;if(result.integrationType==="sdk"){// SDK 集成:检测 SDK 方法调用
91
- const sdkMethod=Object.keys(SDK_TO_TRACKING_MAP).find(key=>SDK_TO_TRACKING_MAP[key]===method.name);if(sdkMethod){found=detectSDKCall(allContent,sdkMethod)}}else if(result.integrationType==="tracking"){// 直接 tracking 集成:检测 tracking 方法调用
92
- found=detectDirectTrackingCall(allContent,method.name)}result.methods.push({method:method.name,found,required:method.required,description:method.description});// 必须方法未找到 -> 错误
93
- if(method.required&&!found){result.errors.push(`❌ 缺少必须的埋点方法: ${method.name} (${method.description})`)}// 可选方法未找到 -> 警告
94
- if(!method.required&&!found){result.warnings.push(`⚠️ 未找到可选埋点方法: ${method.name} (${method.description})`)}}// 5. 判断验证结果
95
- result.success=result.errors.length===0;return result}// ============================================================
246
+ */
247
+ function validateTracking(projectRoot, options = {}) {
248
+ const {
249
+ extensions = ['.ts', '.tsx', '.js', '.jsx'],
250
+ excludeDirs = ['node_modules', 'dist', 'build', '.git'],
251
+ verbose = false
252
+ } = options;
253
+
254
+ const result = {
255
+ success: false,
256
+ integrationType: 'none',
257
+ methods: [],
258
+ errors: [],
259
+ warnings: [],
260
+ stats: {
261
+ totalFiles: 0,
262
+ scannedFiles: 0,
263
+ hasTrackingImport: false,
264
+ hasSDKImport: false
265
+ }
266
+ };
267
+
268
+ // 1. 扫描所有源码文件
269
+ if (verbose) console.log(`\n[扫描] 开始扫描项目: ${projectRoot}`);
270
+ const files = scanSourceFiles(projectRoot, extensions, excludeDirs);
271
+ result.stats.totalFiles = files.length;
272
+
273
+ if (files.length === 0) {
274
+ result.errors.push('未找到任何源码文件');
275
+ return result;
276
+ }
277
+
278
+ if (verbose) console.log(`[扫描] 找到 ${files.length} 个源码文件\n`);
279
+
280
+ // 2. 读取所有文件内容并合并(用于全局检测)
281
+ let allContent = '';
282
+ for (const file of files) {
283
+ const content = readFileContent(file);
284
+ if (content) {
285
+ allContent += content + '\n';
286
+ result.stats.scannedFiles++;
287
+ }
288
+ }
289
+
290
+ // 3. 判断集成类型
291
+ result.stats.hasTrackingImport = hasTrackingImport(allContent);
292
+ result.stats.hasSDKImport = hasSDKImport(allContent);
293
+
294
+ if (result.stats.hasTrackingImport && result.stats.hasSDKImport) {
295
+ result.integrationType = 'sdk'; // SDK 优先
296
+ } else if (result.stats.hasSDKImport) {
297
+ result.integrationType = 'sdk';
298
+ } else if (result.stats.hasTrackingImport) {
299
+ result.integrationType = 'tracking';
300
+ } else {
301
+ result.errors.push('未检测到 @playcraft/adsdk 或 @playcraft/ads-tracking 的导入');
302
+ return result;
303
+ }
304
+
305
+ if (verbose) {
306
+ console.log(`[集成类型] ${result.integrationType === 'sdk' ? 'SDK 集成(通过 sdk 静态方法调用)' : '直接 tracking 集成'}`);
307
+ console.log(`[导入检测] SDK: ${result.stats.hasSDKImport ? '✓' : '✗'} | Tracking: ${result.stats.hasTrackingImport ? '✓' : '✗'}\n`);
308
+ }
309
+
310
+ // 4. 检测每个埋点方法
311
+ for (const method of TRACKING_METHODS) {
312
+ let found = false;
313
+
314
+ if (result.integrationType === 'sdk') {
315
+ // SDK 集成:检测 SDK 方法调用
316
+ const sdkMethod = Object.keys(SDK_TO_TRACKING_MAP).find(
317
+ key => SDK_TO_TRACKING_MAP[key] === method.name
318
+ );
319
+ if (sdkMethod) {
320
+ found = detectSDKCall(allContent, sdkMethod);
321
+ }
322
+ } else if (result.integrationType === 'tracking') {
323
+ // 直接 tracking 集成:检测 tracking 方法调用
324
+ found = detectDirectTrackingCall(allContent, method.name);
325
+ }
326
+
327
+ result.methods.push({
328
+ method: method.name,
329
+ found,
330
+ required: method.required,
331
+ description: method.description
332
+ });
333
+
334
+ // 必须方法未找到 -> 错误
335
+ if (method.required && !found) {
336
+ result.errors.push(`❌ 缺少必须的埋点方法: ${method.name} (${method.description})`);
337
+ }
338
+
339
+ // 可选方法未找到 -> 警告
340
+ if (!method.required && !found) {
341
+ result.warnings.push(`⚠️ 未找到可选埋点方法: ${method.name} (${method.description})`);
342
+ }
343
+ }
344
+
345
+ // 5. 判断验证结果
346
+ result.success = result.errors.length === 0;
347
+
348
+ return result;
349
+ }
350
+
351
+ // ============================================================
96
352
  // 格式化输出
97
353
  // ============================================================
354
+
98
355
  /**
99
356
  * 格式化验证结果并输出到控制台
100
357
  * @param {ValidationResult} result - 验证结果
101
358
  * @param {boolean} [showDetails=true] - 是否显示详细信息
102
- */function printValidationResult(result,showDetails=true){console.log("\n"+"=".repeat(60));console.log("\uD83D\uDCCA \u57CB\u70B9\u9A8C\u8BC1\u62A5\u544A");console.log("=".repeat(60));// 统计信息
103
- console.log(`\n📁 文件统计: 扫描 ${result.stats.scannedFiles}/${result.stats.totalFiles} 个文件`);console.log(`🔗 集成类型: ${result.integrationType==="sdk"?"SDK \u96C6\u6210":result.integrationType==="tracking"?"Tracking \u76F4\u63A5\u96C6\u6210":"\u672A\u68C0\u6D4B\u5230"}`);// 方法检测结果
104
- if(showDetails){console.log("\n\uD83D\uDCCB \u65B9\u6CD5\u68C0\u6D4B\u660E\u7EC6:");console.log("-".repeat(60));const requiredMethods=result.methods.filter(m=>m.required);const optionalMethods=result.methods.filter(m=>!m.required);console.log("\n\u5FC5\u987B\u65B9\u6CD5:");for(const m of requiredMethods){const status=m.found?"\u2705":"\u274C";console.log(` ${status} ${m.method.padEnd(35)} ${m.found?"\u5DF2\u8C03\u7528":"\u672A\u8C03\u7528"}`)}console.log("\n\u53EF\u9009\u65B9\u6CD5:");for(const m of optionalMethods){const status=m.found?"\u2705":"\u26A0\uFE0F ";console.log(` ${status} ${m.method.padEnd(35)} ${m.found?"\u5DF2\u8C03\u7528":"\u672A\u8C03\u7528"}`)}}// 错误信息
105
- if(result.errors.length>0){console.log("\n\u274C \u9519\u8BEF:");result.errors.forEach(err=>console.log(` ${err}`))}// 警告信息
106
- if(result.warnings.length>0){console.log("\n\u26A0\uFE0F \u8B66\u544A:");result.warnings.forEach(warn=>console.log(` ${warn}`))}// 最终结果
107
- console.log("\n"+"=".repeat(60));if(result.success){console.log("\u2705 \u9A8C\u8BC1\u901A\u8FC7\uFF01\u6240\u6709\u5FC5\u987B\u7684\u57CB\u70B9\u65B9\u6CD5\u90FD\u5DF2\u6B63\u786E\u8C03\u7528\u3002")}else{console.log("\u274C \u9A8C\u8BC1\u5931\u8D25\uFF01\u5B58\u5728\u7F3A\u5931\u7684\u5FC5\u987B\u57CB\u70B9\u65B9\u6CD5\uFF0C\u8BF7\u68C0\u67E5\u4EE3\u7801\u3002")}console.log("=".repeat(60)+"\n")}// ============================================================
359
+ */
360
+ function printValidationResult(result, showDetails = true) {
361
+ console.log('\n' + '='.repeat(60));
362
+ console.log('📊 埋点验证报告');
363
+ console.log('='.repeat(60));
364
+
365
+ // 统计信息
366
+ console.log(`\n📁 文件统计: 扫描 ${result.stats.scannedFiles}/${result.stats.totalFiles} 个文件`);
367
+ console.log(`🔗 集成类型: ${result.integrationType === 'sdk' ? 'SDK 集成' : result.integrationType === 'tracking' ? 'Tracking 直接集成' : '未检测到'}`);
368
+
369
+ // 方法检测结果
370
+ if (showDetails) {
371
+ console.log('\n📋 方法检测明细:');
372
+ console.log('-'.repeat(60));
373
+
374
+ const requiredMethods = result.methods.filter(m => m.required);
375
+ const optionalMethods = result.methods.filter(m => !m.required);
376
+
377
+ console.log('\n必须方法:');
378
+ for (const m of requiredMethods) {
379
+ const status = m.found ? '✅' : '❌';
380
+ console.log(` ${status} ${m.method.padEnd(35)} ${m.found ? '已调用' : '未调用'}`);
381
+ }
382
+
383
+ console.log('\n可选方法:');
384
+ for (const m of optionalMethods) {
385
+ const status = m.found ? '✅' : '⚠️ ';
386
+ console.log(` ${status} ${m.method.padEnd(35)} ${m.found ? '已调用' : '未调用'}`);
387
+ }
388
+ }
389
+
390
+ // 错误信息
391
+ if (result.errors.length > 0) {
392
+ console.log('\n❌ 错误:');
393
+ result.errors.forEach(err => console.log(` ${err}`));
394
+ }
395
+
396
+ // 警告信息
397
+ if (result.warnings.length > 0) {
398
+ console.log('\n⚠️ 警告:');
399
+ result.warnings.forEach(warn => console.log(` ${warn}`));
400
+ }
401
+
402
+ // 最终结果
403
+ console.log('\n' + '='.repeat(60));
404
+ if (result.success) {
405
+ console.log('✅ 验证通过!所有必须的埋点方法都已正确调用。');
406
+ } else {
407
+ console.log('❌ 验证失败!存在缺失的必须埋点方法,请检查代码。');
408
+ }
409
+ console.log('='.repeat(60) + '\n');
410
+ }
411
+
412
+ // ============================================================
108
413
  // 导出
109
414
  // ============================================================
110
- module.exports={// 核心验证函数
111
- validateTracking,printValidationResult,// 配置数据(供外部使用)
112
- TRACKING_METHODS,SDK_TO_TRACKING_MAP,// 工具函数(供外部使用)
113
- scanSourceFiles,detectDirectTrackingCall,detectSDKCall,hasTrackingImport,hasSDKImport};
415
+
416
+ module.exports = {
417
+ // 核心验证函数
418
+ validateTracking,
419
+ printValidationResult,
420
+
421
+ // 配置数据(供外部使用)
422
+ TRACKING_METHODS,
423
+ SDK_TO_TRACKING_MAP,
424
+
425
+ // 工具函数(供外部使用)
426
+ scanSourceFiles,
427
+ detectDirectTrackingCall,
428
+ detectSDKCall,
429
+ hasTrackingImport,
430
+ hasSDKImport
431
+ };