@ohos-cpf/3rdloop 0.0.2 → 0.0.4

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 (393) hide show
  1. package/README.md +290 -244
  2. package/bin/3rdloop.mjs +46 -46
  3. package/lib/cli.js +781 -781
  4. package/lib/config-cmd.js +451 -451
  5. package/lib/config.js +318 -318
  6. package/lib/doctor.js +113 -113
  7. package/lib/exit-codes.js +30 -30
  8. package/lib/opencode.js +155 -155
  9. package/lib/orch.js +426 -426
  10. package/lib/queries.js +263 -263
  11. package/lib/step.js +325 -325
  12. package/lib/ui.js +222 -222
  13. package/lib/update.js +279 -279
  14. package/lib/workflow.js +308 -308
  15. package/lib/workflows.js +131 -131
  16. package/package.json +50 -50
  17. package/vendor/Server/Agent/SkillSelector/README.md +343 -343
  18. package/vendor/Server/Agent/SkillSelector/core/Agent.js +207 -207
  19. package/vendor/Server/Agent/SkillSelector/index.js +14 -14
  20. package/vendor/Server/Agent/SkillSelector/llm/llmClient.js +313 -313
  21. package/vendor/Server/Agent/SkillSelector/llm/prompts.js +161 -161
  22. package/vendor/Server/Agent/SkillSelector/llm/reactSteps.js +144 -144
  23. package/vendor/Server/Agent/SkillSelector/matcher/fuzzyMatcher.js +433 -433
  24. package/vendor/Server/Agent/SkillSelector/scanner/skillScanner.js +382 -382
  25. package/vendor/Server/Agent/SkillSelector/utils/actionWords.js +59 -59
  26. package/vendor/Server/Brain/AnalysisStrategies.js +422 -422
  27. package/vendor/Server/Brain/Brain.js +686 -686
  28. package/vendor/Server/Brain/DecompositionStrategies.js +621 -621
  29. package/vendor/Server/Brain/PlanPersistence.js +196 -196
  30. package/vendor/Server/Brain/SkillExecutor.js +251 -251
  31. package/vendor/Server/Brain/index.js +11 -11
  32. package/vendor/Server/CLI/cli.js +275 -275
  33. package/vendor/Server/CLI/deveco-code/index.js +71 -71
  34. package/vendor/Server/CLI/deveco-code.js +5 -5
  35. package/vendor/Server/CLI/opencode/index.js +817 -818
  36. package/vendor/Server/CLI/opencode.js +5 -5
  37. package/vendor/Server/DbUse/MessageArchive.js +61 -61
  38. package/vendor/Server/DbUse/StorageManager.js +107 -107
  39. package/vendor/Server/DbUse/adapters/BaseAdapter.js +94 -94
  40. package/vendor/Server/DbUse/adapters/FileAdapter.js +138 -138
  41. package/vendor/Server/DbUse/adapters/JsonAdapter.js +138 -138
  42. package/vendor/Server/DbUse/adapters/JsonlAdapter.js +114 -114
  43. package/vendor/Server/DbUse/adapters/SqliteAdapter.js +157 -157
  44. package/vendor/Server/DbUse/index.js +160 -160
  45. package/vendor/Server/DbUse/stores/SessionStore.js +279 -279
  46. package/vendor/Server/DbUse/stores/TaskExecutionStore.js +133 -133
  47. package/vendor/Server/DbUse/stores/TaskRegistryStore.js +262 -262
  48. package/vendor/Server/DbUse/stores/TaskStepStore.js +279 -279
  49. package/vendor/Server/FlexRunner/FlexRunner.js +1759 -1759
  50. package/vendor/Server/FlexRunner/index.js +12 -12
  51. package/vendor/Server/Knowledge/KnowledgeImporter.js +523 -523
  52. package/vendor/Server/Knowledge/KnowledgeManager.js +341 -341
  53. package/vendor/Server/Knowledge/index.js +33 -33
  54. package/vendor/Server/LoopEngine/LoopEngine.js +1133 -1133
  55. package/vendor/Server/LoopEngine/index.js +11 -11
  56. package/vendor/Server/Orchestrator/Orchestrator.js +0 -3
  57. package/vendor/Server/Orchestrator/StepNode.js +312 -312
  58. package/vendor/Server/Orchestrator/index.js +13 -13
  59. package/vendor/Server/Routes/controllers/BrainController.js +241 -241
  60. package/vendor/Server/Routes/controllers/FlexRunnerController.js +1125 -1125
  61. package/vendor/Server/Routes/controllers/HealthController.js +44 -44
  62. package/vendor/Server/Routes/controllers/KnowledgeImportController.js +355 -355
  63. package/vendor/Server/Routes/controllers/LoopEngineController.js +788 -788
  64. package/vendor/Server/Routes/controllers/StatsController.js +57 -57
  65. package/vendor/Server/Routes/controllers/StreamController.js +67 -67
  66. package/vendor/Server/Routes/library/logger.js +179 -179
  67. package/vendor/Server/Routes/library/zipArchive.js +172 -172
  68. package/vendor/Server/Routes/router/Router.js +204 -204
  69. package/vendor/Server/Routes/routes/brain.js +47 -47
  70. package/vendor/Server/Routes/routes/flexrunner.js +72 -72
  71. package/vendor/Server/Routes/routes/health.js +28 -28
  72. package/vendor/Server/Routes/routes/index.js +57 -57
  73. package/vendor/Server/Routes/routes/knowledge.js +55 -55
  74. package/vendor/Server/Routes/routes/loopengine.js +58 -58
  75. package/vendor/Server/Routes/routes/orchestrator.js +61 -61
  76. package/vendor/Server/Routes/routes/registry.js +97 -97
  77. package/vendor/Server/Routes/routes/stats.js +23 -23
  78. package/vendor/Server/Routes/routes/stream.js +30 -30
  79. package/vendor/Server/Routes/server.js +203 -203
  80. package/vendor/Server/Routes/sse/SseHub.js +308 -308
  81. package/vendor/Server/Skills/acceptance-check/SKILL.md +191 -191
  82. package/vendor/Server/Skills/acceptance-check/references/PHASE1_ARTIFACT_COLLECTION.md +52 -52
  83. package/vendor/Server/Skills/acceptance-check/references/PHASE2_CRITERIA_EVALUATION.md +77 -77
  84. package/vendor/Server/Skills/acceptance-check/references/PHASE2_EVIDENCE_VERIFICATION.md +70 -70
  85. package/vendor/Server/Skills/arkts-code-check/SKILL.md +320 -320
  86. package/vendor/Server/Skills/arkts-code-check/references/CODE_CHECK_GUIDE.md +210 -210
  87. package/vendor/Server/Skills/arkts-code-check/references/FILE_TYPE_GUIDE.md +28 -28
  88. package/vendor/Server/Skills/arkts-code-check/references/PHASE1_INFO_COLLECTION.md +233 -233
  89. package/vendor/Server/Skills/arkts-code-check/references/REPORT_TEMPLATE.md +219 -219
  90. package/vendor/Server/Skills/arkts-library-compile-validate/SKILL.md +328 -328
  91. package/vendor/Server/Skills/arkts-library-compile-validate/scripts/build.ps1 +93 -93
  92. package/vendor/Server/Skills/arkts-library-compile-validate/scripts/code-linter.cjs +432 -432
  93. package/vendor/Server/Skills/arkts-library-compile-validate/scripts/migrate.cjs +851 -851
  94. package/vendor/Server/Skills/arkts-library-compile-validate/scripts/ohpm-switch.cjs +788 -788
  95. package/vendor/Server/Skills/arkts-library-demo-code/SKILL.md +291 -291
  96. package/vendor/Server/Skills/arkts-library-demo-code/references/API_SIGNATURE_CHECKS.md +68 -68
  97. package/vendor/Server/Skills/arkts-library-demo-code/references/BUILD_VALIDATION.md +184 -184
  98. package/vendor/Server/Skills/arkts-library-demo-code/references/CODE_STANDARDS.md +337 -337
  99. package/vendor/Server/Skills/arkts-library-demo-code/references/EXAMPLES.md +400 -400
  100. package/vendor/Server/Skills/arkts-library-demo-code/references/SYSTEM_API_LOOKUP.md +98 -98
  101. package/vendor/Server/Skills/arkts-library-demo-code/references/TEMPLATE.md +544 -544
  102. package/vendor/Server/Skills/arkts-library-demo-code/scripts/build.ps1 +93 -93
  103. package/vendor/Server/Skills/arkts-library-demo-code/scripts/code-linter.cjs +432 -432
  104. package/vendor/Server/Skills/arkts-library-demo-code/scripts/migrate.cjs +851 -851
  105. package/vendor/Server/Skills/arkts-library-demo-code/scripts/ohpm-switch.cjs +788 -788
  106. package/vendor/Server/Skills/arkts-library-demo-coverage/SKILL.md +174 -174
  107. package/vendor/Server/Skills/arkts-library-demo-scenario/SKILL.md +321 -321
  108. package/vendor/Server/Skills/arkts-library-demo-scenario/references/EXAMPLES.md +100 -100
  109. package/vendor/Server/Skills/arkts-library-demo-scenario/references/TEMPLATE.md +185 -185
  110. package/vendor/Server/Skills/arkts-library-documentation-quality-check/SKILL.md +233 -233
  111. package/vendor/Server/Skills/arkts-library-documentation-quality-check/assets/REPORT_TEMPLATE.md +102 -102
  112. package/vendor/Server/Skills/arkts-library-documentation-quality-check/references/CHECK_DIMENSIONS.md +89 -89
  113. package/vendor/Server/Skills/arkts-library-documentation-quality-check/references/COMPLIANCE_CHECKLIST.md +62 -62
  114. package/vendor/Server/Skills/arkts-library-documentation-quality-check/references/KB_QUERY_GUIDE.md +107 -107
  115. package/vendor/Server/Skills/arkts-library-documentation-quality-check/references/MCP_TOOL_DISCOVERY.md +124 -124
  116. package/vendor/Server/Skills/arkts-library-documentation-quality-check/references/REPORT_TEMPLATE.md +90 -90
  117. package/vendor/Server/Skills/arkts-library-documentation-quality-check/references/SCORING_FORMULA.md +57 -57
  118. package/vendor/Server/Skills/arkts-library-documentation-quality-check/scripts/validate-skill.cjs +259 -259
  119. package/vendor/Server/Skills/arkts-library-interface-spec/SKILL.md +97 -97
  120. package/vendor/Server/Skills/arkts-library-memory-leak-check/SKILL.md +391 -391
  121. package/vendor/Server/Skills/arkts-library-source-clone/SKILL.md +113 -113
  122. package/vendor/Server/Skills/arkts-library-stability-check/SKILL.md +312 -312
  123. package/vendor/Server/Skills/arkts-library-stability-check/assets/REPORT_TEMPLATE.md +154 -154
  124. package/vendor/Server/Skills/arkts-library-stability-check/references/CHECKLIST.md +165 -165
  125. package/vendor/Server/Skills/arkts-library-stability-check/references/CODE_EXAMPLES.md +308 -308
  126. package/vendor/Server/Skills/arkts-library-stability-check/references/SCORING_RULES.md +90 -90
  127. package/vendor/Server/Skills/arkts-library-stability-check/scripts/validate-skill.cjs +282 -282
  128. package/vendor/Server/Skills/arkts-library-test-coverage-check/SKILL.md +360 -360
  129. package/vendor/Server/Skills/arkts-library-test-coverage-check/assets/REPORT_TEMPLATE.md +145 -145
  130. package/vendor/Server/Skills/arkts-library-test-coverage-check/references/CHECKLIST.md +252 -252
  131. package/vendor/Server/Skills/arkts-library-test-coverage-check/references/MCP_TOOL_USAGE.md +114 -114
  132. package/vendor/Server/Skills/arkts-library-test-coverage-check/scripts/validate-skill.cjs +262 -262
  133. package/vendor/Server/Skills/arkts-library-xts-code/SKILL.md +455 -455
  134. package/vendor/Server/Skills/arkts-library-xts-code/assets/report-template.md +60 -60
  135. package/vendor/Server/Skills/arkts-library-xts-code/references/ACCESS_MODIFIER_PRECHECK.md +76 -76
  136. package/vendor/Server/Skills/arkts-library-xts-code/references/BUILD_VERIFICATION.md +200 -200
  137. package/vendor/Server/Skills/arkts-library-xts-code/references/CODE_STANDARDS.md +518 -518
  138. package/vendor/Server/Skills/arkts-library-xts-code/references/SUBAGENT_PROMPT_TEMPLATE.md +276 -276
  139. package/vendor/Server/Skills/arkts-library-xts-code/references/SYSTEM_API_LOOKUP.md +92 -92
  140. package/vendor/Server/Skills/arkts-library-xts-code/references/XTS_TEMPLATE.md +767 -767
  141. package/vendor/Server/Skills/arkts-library-xts-code/scripts/build.ps1 +93 -93
  142. package/vendor/Server/Skills/arkts-library-xts-code/scripts/code-linter.cjs +432 -432
  143. package/vendor/Server/Skills/arkts-library-xts-code/scripts/migrate.cjs +851 -851
  144. package/vendor/Server/Skills/arkts-library-xts-code/scripts/ohpm-switch.cjs +788 -788
  145. package/vendor/Server/Skills/arkts-library-xts-code/scripts/precheck-access-modifiers.cjs +221 -221
  146. package/vendor/Server/Skills/arkts-library-xts-code-fix/SKILL.md +228 -228
  147. package/vendor/Server/Skills/arkts-library-xts-coverage/SKILL.md +97 -97
  148. package/vendor/Server/Skills/arkts-library-xts-doc/SKILL.md +194 -194
  149. package/vendor/Server/Skills/arkts-library-xts-doc/references/EXAMPLES.md +575 -575
  150. package/vendor/Server/Skills/arkts-library-xts-doc/references/TEMPLATE.md +574 -574
  151. package/vendor/Server/Skills/arkts-library-xts-doc/references/TESTABILITY_GUIDE.md +182 -182
  152. package/vendor/Server/Skills/arkts-library-xts-execution/SKILL.md +490 -490
  153. package/vendor/Server/Skills/arkts-library-xts-execution/references/error-fixing-examples.md +217 -217
  154. package/vendor/Server/Skills/arkts-library-xts-execution/scripts/build.ps1 +93 -93
  155. package/vendor/Server/Skills/arkts-library-xts-execution/scripts/code-linter.cjs +432 -432
  156. package/vendor/Server/Skills/arkts-library-xts-execution/scripts/migrate.cjs +851 -851
  157. package/vendor/Server/Skills/arkts-library-xts-execution/scripts/ohpm-switch.cjs +788 -788
  158. package/vendor/Server/Skills/flutter-code-check/SKILL.md +299 -299
  159. package/vendor/Server/Skills/flutter-code-check/assets/OUTPUT_TEMPLATE.md +311 -311
  160. package/vendor/Server/Skills/flutter-code-check/references/CHECKLIST.md +190 -190
  161. package/vendor/Server/Skills/flutter-code-check/references/PLATFORMS.md +133 -133
  162. package/vendor/Server/Skills/flutter-code-check/scripts/validate-skill.cjs +259 -259
  163. package/vendor/Server/Skills/flutter-interface/README.md +75 -75
  164. package/vendor/Server/Skills/flutter-interface/SKILL.md +291 -291
  165. package/vendor/Server/Skills/flutter-interface/references/EXAMPLES.md +336 -336
  166. package/vendor/Server/Skills/flutter-interface/references/GIT_CLONE_GUIDE.md +190 -190
  167. package/vendor/Server/Skills/flutter-interface/references/SCRIPT_REFERENCE.md +258 -258
  168. package/vendor/Server/Skills/flutter-interface/references/TEMPLATE.md +254 -254
  169. package/vendor/Server/Skills/flutter-library-consistency-check/SKILL.md +517 -517
  170. package/vendor/Server/Skills/flutter-library-consistency-check/references/CHECKLIST.md +381 -381
  171. package/vendor/Server/Skills/flutter-library-consistency-check/references/FUNCTION_COMPARISON_GUIDE.md +341 -341
  172. package/vendor/Server/Skills/flutter-library-consistency-check/references/PLATFORM_LIMITATION_GUIDE.md +71 -71
  173. package/vendor/Server/Skills/flutter-library-documentation-quality-check/SKILL.md +518 -518
  174. package/vendor/Server/Skills/flutter-library-documentation-quality-check/references/CHECKLIST.md +406 -406
  175. package/vendor/Server/Skills/flutter-library-memory-leak-check/SKILL.md +652 -652
  176. package/vendor/Server/Skills/flutter-library-memory-leak-check/references/CHECKLIST.md +396 -396
  177. package/vendor/Server/Skills/flutter-library-stability-check/SKILL.md +744 -744
  178. package/vendor/Server/Skills/flutter-library-stability-check/references/CHECKLIST.md +720 -720
  179. package/vendor/Server/Skills/flutter-library-test-coverage-check/SKILL.md +414 -414
  180. package/vendor/Server/Skills/flutter-library-test-coverage-check/assets/REPORT_TEMPLATE.md +150 -150
  181. package/vendor/Server/Skills/flutter-library-test-coverage-check/references/CHECKLIST.md +304 -304
  182. package/vendor/Server/Skills/flutter-library-test-coverage-check/references/SCORING_RULES.md +265 -265
  183. package/vendor/Server/Skills/flutter-library-test-coverage-check/references/SCRIPT_USAGE.md +286 -286
  184. package/vendor/Server/Skills/knowledge-import/SKILL.md +383 -383
  185. package/vendor/Server/Skills/knowledge-import/references/MCP_TOOL_USAGE.md +165 -165
  186. package/vendor/Server/Skills/knowledge-import/references/SKILL_LOCATOR_GUIDE.md +91 -91
  187. package/vendor/Server/Skills/knowledge-import/references/SKILL_OPTIMIZATION_GUIDE.md +127 -127
  188. package/vendor/Server/Skills/ohos-lib-pr-push/SKILL.md +453 -301
  189. package/vendor/Server/Skills/ohos-lib-pr-push/assets/PR_CHECKLIST.md +151 -0
  190. package/vendor/Server/Skills/ohos-lib-pr-push/assets/PR_ISSUE_TEMPLATE.md +257 -105
  191. package/vendor/Server/Skills/ohos-lib-pr-push/references/GITCODE_API.md +191 -127
  192. package/vendor/Server/Skills/ohos-lib-pr-push/scripts/gitcode-api.cjs +317 -208
  193. package/vendor/Server/Skills/rn-code-check/SKILL.md +320 -320
  194. package/vendor/Server/Skills/rn-code-check/references/API-GUIDE.md +234 -234
  195. package/vendor/Server/Skills/rn-code-check/references/CHECKLIST.md +188 -188
  196. package/vendor/Server/Skills/rn-code-check/references/REPORT-TEMPLATE.md +246 -246
  197. package/vendor/Server/Skills/rn-lib-add-example/SKILL.md +198 -198
  198. package/vendor/Server/Skills/rn-lib-add-example/references/APP_RENAME_GUIDE.md +40 -40
  199. package/vendor/Server/Skills/rn-lib-add-example/references/SHELL_ADAPTATION_CHECKLIST.md +78 -78
  200. package/vendor/Server/Skills/rn-lib-add-example/references/TEMPLATE_REPO_GUIDE.md +70 -70
  201. package/vendor/Server/Skills/rn-lib-add-example/references/VERIFICATION_GUIDE.md +47 -47
  202. package/vendor/Server/Skills/rn-lib-add-example/scripts/create-example.cjs +252 -252
  203. package/vendor/Server/Skills/rn-lib-update-doc/SKILL.md +176 -176
  204. package/vendor/Server/Skills/rn-lib-update-doc/config.json +5 -5
  205. package/vendor/Server/Skills/rn-lib-update-doc/references/changelog-template.md +35 -35
  206. package/vendor/Server/Skills/rn-lib-update-doc/references/format-diff.md +137 -137
  207. package/vendor/Server/Skills/rn-lib-update-doc/references/link-registry.json +33 -33
  208. package/vendor/Server/Skills/rn-lib-update-doc/references/model-0.4.2-overview.md +40 -40
  209. package/vendor/Server/Skills/rn-lib-update-doc/references/model-0.4.2.md +429 -429
  210. package/vendor/Server/Skills/rn-lib-update-doc/scripts/detect-readme-format.cjs +105 -105
  211. package/vendor/Server/Skills/rn-lib-update-doc/scripts/find-usage-doc.cjs +90 -90
  212. package/vendor/Server/Skills/rn-lib-update-doc/scripts/link-verify.cjs +213 -213
  213. package/vendor/Server/Skills/rn-lib-update-doc/scripts/normalize-changelog.cjs +73 -73
  214. package/vendor/Server/Skills/rn-lib-update-doc/scripts/normalize-package-meta.cjs +258 -258
  215. package/vendor/Server/Skills/rn-lib-update-doc/scripts/normalize-readme-links.cjs +341 -341
  216. package/vendor/Server/Skills/rn-lib-update-doc/scripts/resolve-usage-docs.cjs +67 -67
  217. package/vendor/Server/Skills/rn-library-consistency-check/SKILL.md +299 -299
  218. package/vendor/Server/Skills/rn-library-consistency-check/assets/REPORT_TEMPLATE.md +293 -293
  219. package/vendor/Server/Skills/rn-library-consistency-check/references/ARKTS_COMPONENT_CHECK_GUIDE.md +857 -857
  220. package/vendor/Server/Skills/rn-library-consistency-check/references/ARKTS_TURBOMODULE_CHECK_GUIDE.md +856 -856
  221. package/vendor/Server/Skills/rn-library-consistency-check/references/CHECKLIST.md +157 -157
  222. package/vendor/Server/Skills/rn-library-consistency-check/references/CPP_COMPONENT_CHECK_GUIDE.md +801 -801
  223. package/vendor/Server/Skills/rn-library-consistency-check/references/CPP_TURBOMODULE_CHECK_GUIDE.md +683 -683
  224. package/vendor/Server/Skills/rn-library-consistency-check/references/FUNCTION_COMPARISON_GUIDE.md +233 -233
  225. package/vendor/Server/Skills/rn-library-consistency-check/references/NATIVE_COMPONENT_IDENTIFICATION.md +1373 -1373
  226. package/vendor/Server/Skills/rn-library-consistency-check/references/SCORING_STANDARD.md +220 -220
  227. package/vendor/Server/Skills/rn-library-consistency-check/scripts/validate-skill.py +245 -245
  228. package/vendor/Server/Skills/rn-library-documentation-quality-check/SKILL.md +263 -263
  229. package/vendor/Server/Skills/rn-library-documentation-quality-check/assets/MODEL.md +414 -414
  230. package/vendor/Server/Skills/rn-library-documentation-quality-check/assets/MODEL_en.md +413 -413
  231. package/vendor/Server/Skills/rn-library-documentation-quality-check/assets/REPORT_TEMPLATE.md +311 -311
  232. package/vendor/Server/Skills/rn-library-documentation-quality-check/references/CHECK_CHECKLIST.md +355 -355
  233. package/vendor/Server/Skills/rn-library-documentation-quality-check/references/CROSS_VALIDATION_GUIDE.md +115 -115
  234. package/vendor/Server/Skills/rn-library-documentation-quality-check/references/DOC_COLLECTION_GUIDE.md +139 -139
  235. package/vendor/Server/Skills/rn-library-documentation-quality-check/references/EXAMPLES.md +586 -586
  236. package/vendor/Server/Skills/rn-library-documentation-quality-check/references/GOTCHAS.md +57 -57
  237. package/vendor/Server/Skills/rn-library-documentation-quality-check/scripts/validate-skill.py +259 -259
  238. package/vendor/Server/Skills/rn-library-interface-analyzer/SKILL.md +301 -301
  239. package/vendor/Server/Skills/rn-library-interface-analyzer/assets/REPORT_TEMPLATE.md +148 -148
  240. package/vendor/Server/Skills/rn-library-interface-analyzer/references/DOC_ANNOTATION_GUIDE.md +118 -118
  241. package/vendor/Server/Skills/rn-library-interface-analyzer/references/SCRIPT_USAGE_GUIDE.md +168 -168
  242. package/vendor/Server/Skills/rn-library-interface-analyzer/references/URL_PARSING_RULES.md +176 -176
  243. package/vendor/Server/Skills/rn-library-issue-generalizer/SKILL.md +274 -274
  244. package/vendor/Server/Skills/rn-library-issue-generalizer/assets/case-template.md +45 -45
  245. package/vendor/Server/Skills/rn-library-issue-generalizer/assets/rule-template.md +25 -25
  246. package/vendor/Server/Skills/rn-library-issue-generalizer/references/PR_FETCH_GUIDE.md +61 -61
  247. package/vendor/Server/Skills/rn-library-issue-generalizer/references/RULE_TEMPLATE_GUIDE.md +165 -165
  248. package/vendor/Server/Skills/rn-library-issue-generalizer/scripts/fetch-pr-info.py +239 -239
  249. package/vendor/Server/Skills/rn-library-known-issue-check/SKILL.md +263 -263
  250. package/vendor/Server/Skills/rn-library-known-issue-check/assets/REPORT_TEMPLATE.md +96 -96
  251. package/vendor/Server/Skills/rn-library-known-issue-check/references/COMPLIANCE_CHECKLIST.md +57 -57
  252. package/vendor/Server/Skills/rn-library-known-issue-check/references/INSPECTION_GUIDE.md +99 -99
  253. package/vendor/Server/Skills/rn-library-known-issue-check/references/PRESCREEN_GUIDE.md +64 -64
  254. package/vendor/Server/Skills/rn-library-known-issue-check/references/SOURCE_PREP_GUIDE.md +74 -74
  255. package/vendor/Server/Skills/rn-library-known-issue-check/scripts/validate-known-issue-report.py +164 -164
  256. package/vendor/Server/Skills/rn-library-memory-leak-check/SKILL.md +356 -339
  257. package/vendor/Server/Skills/rn-library-memory-leak-check/assets/REPORT_TEMPLATE.md +85 -83
  258. package/vendor/Server/Skills/rn-library-memory-leak-check/references/CHECKLIST.md +905 -859
  259. package/vendor/Server/Skills/rn-library-memory-leak-check/references/HARMONY_CODE_PATTERNS.md +124 -122
  260. package/vendor/Server/Skills/rn-library-memory-leak-check/scripts/scan_harmony_code.py +287 -279
  261. package/vendor/Server/Skills/rn-library-stability-check/SKILL.md +273 -273
  262. package/vendor/Server/Skills/rn-library-stability-check/assets/REPORT_TEMPLATE.md +129 -129
  263. package/vendor/Server/Skills/rn-library-stability-check/references/CHECKLIST.md +581 -581
  264. package/vendor/Server/Skills/rn-library-stability-check/references/SCORING_RULES.md +138 -138
  265. package/vendor/Server/Skills/rn-library-stability-check/scripts/scan-harmony-code.py +168 -168
  266. package/vendor/Server/Skills/rn-library-test-coverage-check/SKILL.md +526 -506
  267. package/vendor/Server/Skills/rn-library-test-coverage-check/references/CHECKLIST.md +321 -280
  268. package/vendor/Server/Skills/rn-library-test-coverage-check/references/DEMO_LOCATION.md +221 -221
  269. package/vendor/Server/Skills/rn-library-test-coverage-check/references/REPORT_TEMPLATE.md +220 -209
  270. package/vendor/Server/Skills/rn-library-test-coverage-check/references/SCRIPT_USAGE.md +325 -325
  271. package/vendor/Server/Skills/rn-ohos-xts-generator/SKILL.md +279 -279
  272. package/vendor/Server/Skills/rn-ohos-xts-generator/assets/TEST_FILE_TEMPLATES.md +404 -404
  273. package/vendor/Server/Skills/rn-ohos-xts-generator/references/BUILD_TROUBLESHOOTING.md +185 -185
  274. package/vendor/Server/Skills/rn-ohos-xts-generator/references/BUILD_WORKFLOW.md +265 -265
  275. package/vendor/Server/Skills/rn-ohos-xts-generator/references/COMMON_CONFIG.md +189 -189
  276. package/vendor/Server/Skills/rn-ohos-xts-generator/references/CONFIG_GUIDE.md +203 -203
  277. package/vendor/Server/Skills/rn-ohos-xts-generator/references/COVERAGE_ANALYZER.md +147 -147
  278. package/vendor/Server/Skills/rn-ohos-xts-generator/references/DESIGN_DOC_GUIDE.md +148 -148
  279. package/vendor/Server/Skills/rn-ohos-xts-generator/references/GENERATION_FLOW.md +165 -165
  280. package/vendor/Server/Skills/rn-ohos-xts-generator/references/GENERATION_STRATEGY.md +195 -195
  281. package/vendor/Server/Skills/rn-ohos-xts-generator/references/HARMONY_SCANNER.md +249 -249
  282. package/vendor/Server/Skills/rn-ohos-xts-generator/references/JEST_FRAMEWORK.md +235 -235
  283. package/vendor/Server/Skills/rn-ohos-xts-generator/references/PLATFORM_COMPAT.md +161 -161
  284. package/vendor/Server/Skills/rn-ohos-xts-generator/references/RN_TEST_PATTERNS.md +413 -413
  285. package/vendor/Server/Skills/rn-ohos-xts-generator/references/SOURCE_PARSER.md +235 -235
  286. package/vendor/Server/Skills/rn-ohos-xts-generator/references/TEST_CONVENTIONS.md +245 -245
  287. package/vendor/Server/Skills/rn-ohos-xts-generator/references/TROUBLESHOOTING.md +282 -282
  288. package/vendor/Server/Skills/rn-ohos-xts-generator/references/USAGE_GUIDE.md +251 -251
  289. package/vendor/Server/Skills/rnoh-cptf-api-test/SKILL.md +469 -469
  290. package/vendor/Server/Skills/rnoh-cptf-api-test/assets/CPTF_TEST_TEMPLATE.md +310 -310
  291. package/vendor/Server/Skills/rnoh-cptf-api-test/assets/DEMO_PAGE_TEMPLATE.md +303 -303
  292. package/vendor/Server/Skills/rnoh-cptf-api-test/references/CPTF_TEST_GENERATION_GUIDE.md +421 -421
  293. package/vendor/Server/Skills/rnoh-cptf-api-test/references/DEMO_GENERATION_GUIDE.md +358 -358
  294. package/vendor/Server/Skills/rnoh-cptf-api-test/references/QUALITY_CHECKLIST.md +114 -114
  295. package/vendor/Server/Skills/rnoh-cptf-api-test/references/SPEC_PARSING_GUIDE.md +274 -274
  296. package/vendor/Server/Skills/rnoh-cptf-api-test/scripts/validate-rnoh-cptf-api-test.sh +185 -185
  297. package/vendor/Server/Skills/rnoh-cptf-demo-test/SKILL.md +191 -191
  298. package/vendor/Server/Skills/rnoh-cptf-demo-test/assets/TEST_TEMPLATE.md +216 -216
  299. package/vendor/Server/Skills/rnoh-cptf-demo-test/references/CPTF_SETUP.md +107 -107
  300. package/vendor/Server/Skills/rnoh-cptf-demo-test/references/DEMO_ANALYSIS.md +120 -120
  301. package/vendor/Server/Skills/rnoh-cptf-demo-test/scripts/validate-rnoh-cptf-test.sh +108 -108
  302. package/vendor/Server/Skills/rnoh-cptf-test-run/SKILL.md +237 -237
  303. package/vendor/Server/Skills/rnoh-cptf-test-run/references/DEBUG_PLAYBOOK.md +261 -261
  304. package/vendor/Server/Skills/rnoh-cptf-test-run/scripts/validate-run-env.sh +112 -112
  305. package/vendor/Server/Skills/rnoh-lib-code-adapt/SKILL.md +497 -497
  306. package/vendor/Server/Skills/rnoh-lib-code-adapt/references/adapt-checklist.md +270 -270
  307. package/vendor/Server/Skills/rnoh-lib-code-adapt/references/api-migration-checklist.md +164 -164
  308. package/vendor/Server/Skills/rnoh-lib-code-adapt/references/common-compile-errors.md +556 -556
  309. package/vendor/Server/Skills/rnoh-lib-code-adapt/references/deveco-cli-tools.md +459 -459
  310. package/vendor/Server/Skills/rnoh-lib-code-adapt/references/edge-cases.md +65 -65
  311. package/vendor/Server/Skills/rnoh-lib-code-adapt/references/har-tgz-build.md +512 -512
  312. package/vendor/Server/Skills/rnoh-lib-code-adapt/references/version-compat-matrix.md +132 -132
  313. package/vendor/Server/Skills/rnoh-lib-code-adapt/scripts/auto-integrate.sh +424 -424
  314. package/vendor/Server/Skills/rnoh-lib-demo-coverage/SKILL.md +137 -137
  315. package/vendor/Server/Skills/rnoh-lib-demo-create/SKILL.md +306 -306
  316. package/vendor/Server/Skills/rnoh-lib-demo-create/assets/DEMO_PAGE_TEMPLATE.md +485 -485
  317. package/vendor/Server/Skills/rnoh-lib-demo-create/assets/INDEX_TEMPLATE.md +172 -172
  318. package/vendor/Server/Skills/rnoh-lib-demo-create/references/DEMO_CODE_RULES.md +422 -422
  319. package/vendor/Server/Skills/rnoh-lib-demo-create/references/QUALITY_CHECKLIST.md +95 -95
  320. package/vendor/Server/Skills/rnoh-lib-demo-create/references/SOURCE_EXPLORATION.md +169 -169
  321. package/vendor/Server/Skills/rnoh-lib-demo-doc/SKILL.md +554 -554
  322. package/vendor/Server/Skills/rnoh-lib-demo-doc/assets/DEMO_DOC_TEMPLATE.md +106 -106
  323. package/vendor/Server/Skills/rnoh-lib-demo-doc/references/COVERAGE_INCREMENTAL.md +216 -216
  324. package/vendor/Server/Skills/rnoh-lib-demo-doc/references/DEMO_DESIGN_RULES.md +413 -413
  325. package/vendor/Server/Skills/rnoh-lib-demo-doc/references/OUTPUT_FORMAT.md +269 -269
  326. package/vendor/Server/Skills/rnoh-lib-demo-doc/references/QUALITY_CHECKLIST.md +98 -98
  327. package/vendor/Server/Skills/rnoh-lib-func-test/SKILL.md +337 -337
  328. package/vendor/Server/Skills/rnoh-lib-rnt-search/SKILL.md +722 -722
  329. package/vendor/Server/Skills/rnoh-lib-rnt-search/assets/PORT_REPORT_TEMPLATE.md +219 -219
  330. package/vendor/Server/Skills/rnoh-lib-rnt-search/references/DEMO_PORTING_GUIDE.md +345 -345
  331. package/vendor/Server/Skills/rnoh-lib-rnt-search/references/PORT_VERIFICATION.md +269 -269
  332. package/vendor/Server/Skills/rnoh-lib-rnt-search/references/RNT_PROJECT_STRUCTURE.md +173 -173
  333. package/vendor/Server/Skills/rnoh-lib-rnt-search/scripts/analyze-demo-deps.cjs +267 -267
  334. package/vendor/Server/Skills/rnoh-lib-rnt-search/scripts/detect-rnoh-version.cjs +174 -174
  335. package/vendor/Server/Skills/rnoh-lib-rnt-search/scripts/find-rnt-demo.sh +111 -111
  336. package/vendor/Server/Skills/rnoh-lib-rnt-search/scripts/port-demo.sh +186 -186
  337. package/vendor/Server/Skills/rnoh-lib-rnt-search/scripts/read-lib-meta.cjs +150 -150
  338. package/vendor/Server/Skills/rnoh-lib-test-align/SKILL.md +296 -296
  339. package/vendor/Server/Skills/rnoh-lib-test-align/references/jest-troubleshooting.md +371 -371
  340. package/vendor/Server/Skills/rnoh-lib-test-align/references/port-test-demo-guide.md +212 -212
  341. package/vendor/Server/Skills/rnoh-lib-test-align/references/runtime-troubleshooting.md +345 -345
  342. package/vendor/Server/Skills/rnoh-lib-test-align/references/unit-test-template.md +223 -223
  343. package/vendor/Server/Skills/rnoh-lib-test-align/scripts/port-test-demo.sh +1053 -1053
  344. package/vendor/Server/Skills/rnoh-lib-version-diff/SKILL.md +295 -255
  345. package/vendor/Server/Skills/rnoh-lib-version-diff/references/OHOS_ADAPTATION.md +82 -77
  346. package/vendor/Server/Skills/rnoh-lib-version-diff/references/OHOS_API_LOOKUP.md +224 -224
  347. package/vendor/Server/Skills/rnoh-lib-version-diff/references/script-reference.md +175 -174
  348. package/vendor/Server/Skills/rnoh-lib-version-diff/scripts/detect-upstream-version.js +967 -967
  349. package/vendor/Server/Skills/rnoh-lib-version-diff/scripts/generate-diff.js +1798 -1798
  350. package/vendor/Server/Skills/rnoh-lib-version-diff/scripts/package.json +5 -5
  351. package/vendor/Server/Skills/rnoh-lib-version-diff/templates/report-template.md +180 -137
  352. package/vendor/Server/Skills/rnoh-lib-xts-coverage/SKILL.md +145 -145
  353. package/vendor/Server/Skills/skill-generator/SKILL.md +365 -365
  354. package/vendor/Server/Skills/skill-generator/assets/SKILL_TEMPLATE.md +183 -183
  355. package/vendor/Server/Skills/skill-generator/references/COMPLIANCE_CHECKLIST.md +82 -82
  356. package/vendor/Server/Skills/skill-generator/references/KB_QUERY_GUIDE.md +110 -110
  357. package/vendor/Server/Skills/skill-generator/references/MCP_TOOL_DISCOVERY.md +122 -122
  358. package/vendor/Server/Skills/skill-generator/references/SKILL_SPEC.md +121 -121
  359. package/vendor/Server/Skills/skill-generator/scripts/validate-skill.cjs +259 -259
  360. package/vendor/Server/Skills/task-analysis/SKILL.md +234 -234
  361. package/vendor/Server/Skills/task-analysis/assets/OUTPUT_EXAMPLES.md +160 -160
  362. package/vendor/Server/Skills/task-analysis/references/ACCEPTANCE_CRITERIA_GUIDE.md +190 -190
  363. package/vendor/Server/Skills/task-analysis/references/ANALYSIS_SCHEMA.md +224 -224
  364. package/vendor/Server/Skills/task-analysis/references/INTEGRATION_CONTEXT.md +162 -162
  365. package/vendor/Server/Skills/task-analysis/references/LESSONS_LEARNED.md +55 -55
  366. package/vendor/Server/Skills/task-analysis/references/REPLAN_CONTEXT_GUIDE.md +126 -126
  367. package/vendor/Server/Skills/task-analysis/scripts/validate-analysis-output.sh +256 -256
  368. package/vendor/Server/Skills/task-decomposition/SKILL.md +528 -528
  369. package/vendor/Server/Skills/task-decomposition/assets/OUTPUT_EXAMPLES.md +451 -451
  370. package/vendor/Server/Skills/task-decomposition/references/ACCEPTANCE_CRITERIA_GUIDE.md +179 -179
  371. package/vendor/Server/Skills/task-decomposition/references/DEPENDENCY_GRAPH_GUIDE.md +152 -152
  372. package/vendor/Server/Skills/task-decomposition/references/INTEGRATION_CONTEXT.md +175 -175
  373. package/vendor/Server/Skills/task-decomposition/references/REPLAN_CONTEXT_GUIDE.md +195 -195
  374. package/vendor/Server/Skills/task-decomposition/references/TASK_DECOMPOSITION_TEMPLATES.md +226 -226
  375. package/vendor/Server/Skills/task-decomposition/scripts/list-available-skills.cjs +263 -263
  376. package/vendor/Server/Skills/task-decomposition/scripts/skill-blacklist.json +29 -29
  377. package/vendor/Server/Skills/task-decomposition/scripts/validate-decomposition-output.cjs +376 -376
  378. package/vendor/Server/Skills/task-decomposition/scripts/validate-param-consistency.cjs +259 -259
  379. package/vendor/Server/Skills/task-decomposition/scripts/validate-specified-skills.cjs +216 -216
  380. package/vendor/Server/Skills/task-decomposition/scripts/validate-step-graph.cjs +424 -424
  381. package/vendor/Server/Skills/tpc-cpp-check/SKILL.md +427 -427
  382. package/vendor/Server/Skills/tpc-cpp-check/references/CHECKLIST.md +214 -214
  383. package/vendor/Server/Skills/tpc-cpp-check/references/HPKBUILD_GUIDE.md +235 -235
  384. package/vendor/Server/Skills/tpc-cpp-check/references/REPORT_TEMPLATE.md +250 -250
  385. package/vendor/Server/TestCheck/TestCheck.js +775 -775
  386. package/vendor/Server/TestCheck/index.js +11 -11
  387. package/vendor/Server/library/McpToolGuide.js +280 -280
  388. package/vendor/Server/library/StatsService.js +126 -126
  389. package/vendor/Server/library/cstTime.js +16 -16
  390. package/vendor/VERSION +3 -3
  391. package/workflows/pr-check.mjs +105 -105
  392. package/workflows/pr-push.mjs +192 -192
  393. package/vendor/Server/Agent/SkillSelector/types.js +0 -63
@@ -1,1053 +1,1053 @@
1
- #!/usr/bin/env bash
2
- #
3
- # RNOH 测试 Demo/用例移植脚本
4
- #
5
- # 从 RN 三方库 git 仓库中拆解出测试 demo、用例目录,移植到模板工程中。
6
- #
7
- # 工作流程:
8
- # 1. git clone 仓库到当前目录下的 TestGet/ 目录中
9
- # 2. 自动检测仓库中的测试 demo/用例目录 (example、demo、test、tests、spec 等)
10
- # 3. 如果 demo/用例目录中存在 package.json, 将其依赖的三方库安装到模板工程 (本地依赖)
11
- # 4. 将 demo/用例目录中的所有文件按原有目录结构拷贝到模板工程 src/ 下
12
- # 并将 demo 的入口组件接入到模板工程的 src/index.tsx
13
- #
14
- # 用法:
15
- # ./scripts/port-test-demo.sh --git-url <url> --branch <branch> [选项]
16
- #
17
- # 示例:
18
- # # 移植 lottie-react-native 的测试 demo
19
- # ./scripts/port-test-demo.sh \
20
- # --git-url https://github.com/lottie-react-native/lottie-react-native \
21
- # --branch v7.4.0
22
- #
23
- # # 指定模板版本目录 (默认自动检测 0.77 或 0.82)
24
- # ./scripts/port-test-demo.sh \
25
- # --git-url https://github.com/lottie-react-native/lottie-react-native \
26
- # --branch v7.4.0 \
27
- # --template-dir /Users/lalhan/Desktop/RNOH/RN3rdTemplate/0.77
28
- #
29
- # # 手动指定 demo 目录 (跳过自动检测)
30
- # ./scripts/port-test-demo.sh \
31
- # --git-url https://github.com/lottie-react-native/lottie-react-native \
32
- # --branch v7.4.0 \
33
- # --demo-dirs example
34
- #
35
- # # dry-run 模式 (只检测不实际拷贝)
36
- # ./scripts/port-test-demo.sh \
37
- # --git-url https://github.com/lottie-react-native/lottie-react-native \
38
- # --branch v7.4.0 \
39
- # --dry-run
40
- #
41
-
42
- set -euo pipefail
43
-
44
- # ── 颜色输出 ──────────────────────────────────────────────
45
- RED='\033[0;31m'
46
- GREEN='\033[0;32m'
47
- YELLOW='\033[1;33m'
48
- BLUE='\033[0;34m'
49
- CYAN='\033[0;36m'
50
- NC='\033[0m'
51
-
52
- info() { echo -e "${BLUE}[INFO]${NC} $*"; }
53
- ok() { echo -e "${GREEN}[OK]${NC} $*"; }
54
- warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
55
- error() { echo -e "${RED}[ERROR]${NC} $*" >&2; exit 1; }
56
- debug() { [[ "${VERBOSE:-false}" == "true" ]] && echo -e "${CYAN}[DEBUG]${NC} $*" || true; }
57
-
58
- # ── 默认值 ────────────────────────────────────────────────
59
- GIT_URL=""
60
- GIT_BRANCH=""
61
- TEMPLATE_DIR=""
62
- DEMO_DIRS_OVERRIDE=""
63
- NPM_INSTALL_FLAGS=""
64
- VERBOSE=false
65
- DRY_RUN=false
66
- SKIP_NPM_INSTALL=false
67
- SKIP_INDEX_MERGE=false
68
-
69
- # 已知的 demo/用例目录名 (按常见度排序)
70
- KNOWN_DEMO_DIRS=("example" "demo" "Example" "Demo" "test" "tests" "Test" "Tests" "spec" "specs" "__tests__" "e2e" "cypress")
71
-
72
- # 脚本所在目录 (scripts/)
73
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
74
- # 模板根目录 (包含 0.77/ 0.82/ scripts/ 等)
75
- TEMPLATE_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
76
-
77
- # ── 参数解析 ──────────────────────────────────────────────
78
- usage() {
79
- cat <<'EOF'
80
- RNOH 测试 Demo/用例移植脚本
81
-
82
- 从 RN 三方库 git 仓库中拆解出测试 demo、用例目录,移植到模板工程中。
83
-
84
- 用法:
85
- ./scripts/port-test-demo.sh [选项]
86
-
87
- 必选参数:
88
- --git-url <url> git 仓库 URL (如 https://github.com/lottie-react-native/lottie-react-native)
89
- --branch <branch> git 分支名 (如 v7.4.0)
90
-
91
- 可选参数:
92
- --template-dir <path> 模板工程目录 (如 /path/to/RN3rdTemplate/0.77), 不指定则自动检测
93
- --demo-dirs <dir1,dir2,...> 手动指定 demo 目录名 (逗号分隔, 跳过自动检测)
94
- --legacy-peer-deps npm install 时使用 --legacy-peer-deps
95
- --force npm install 时使用 --force
96
- --skip-npm-install 跳过依赖安装步骤
97
- --skip-index-merge 跳过 index.tsx 合并步骤 (只拷贝文件不修改入口)
98
- --verbose 显示详细调试信息
99
- --dry-run 只检测不实际拷贝
100
- --help, -h 显示帮助
101
-
102
- 示例:
103
- # 移植 lottie-react-native 的测试 demo
104
- ./scripts/port-test-demo.sh \
105
- --git-url https://github.com/lottie-react-native/lottie-react-native \
106
- --branch v7.4.0
107
-
108
- # 手动指定 demo 目录
109
- ./scripts/port-test-demo.sh \
110
- --git-url https://github.com/lottie-react-native/lottie-react-native \
111
- --branch v7.4.0 \
112
- --demo-dirs example
113
-
114
- # dry-run 模式
115
- ./scripts/port-test-demo.sh \
116
- --git-url https://github.com/lottie-react-native/lottie-react-native \
117
- --branch v7.4.0 \
118
- --dry-run
119
- EOF
120
- exit 0
121
- }
122
-
123
- while [[ $# -gt 0 ]]; do
124
- case "$1" in
125
- --git-url) GIT_URL="$2"; shift 2;;
126
- --branch) GIT_BRANCH="$2"; shift 2;;
127
- --template-dir) TEMPLATE_DIR="$2"; shift 2;;
128
- --demo-dirs) DEMO_DIRS_OVERRIDE="$2"; shift 2;;
129
- --legacy-peer-deps) NPM_INSTALL_FLAGS="$NPM_INSTALL_FLAGS --legacy-peer-deps"; shift;;
130
- --force) NPM_INSTALL_FLAGS="$NPM_INSTALL_FLAGS --force"; shift;;
131
- --skip-npm-install) SKIP_NPM_INSTALL=true; shift;;
132
- --skip-index-merge) SKIP_INDEX_MERGE=true; shift;;
133
- --verbose) VERBOSE=true; shift;;
134
- --dry-run) DRY_RUN=true; shift;;
135
- --help|-h) usage;;
136
- *) error "未知参数: $1 (使用 --help 查看用法)";;
137
- esac
138
- done
139
-
140
- # ── 参数校验 ──────────────────────────────────────────────
141
- if [[ -z "$GIT_URL" ]]; then
142
- error "必须指定 --git-url 参数"
143
- fi
144
-
145
- if [[ -z "$GIT_BRANCH" ]]; then
146
- warn "未指定 --branch, 将使用默认分支"
147
- GIT_BRANCH="--"
148
- fi
149
-
150
- # ── 检查依赖 ──────────────────────────────────────────────
151
- for cmd in python3 npm git rsync; do
152
- command -v "$cmd" >/dev/null 2>&1 || error "未找到命令: $cmd, 请先安装"
153
- done
154
-
155
- # ── 自动检测模板工程目录 ──────────────────────────────────
156
- detect_template_dir() {
157
- # 如果用户指定了 --template-dir, 直接使用
158
- if [[ -n "$TEMPLATE_DIR" ]]; then
159
- if [[ ! -d "$TEMPLATE_DIR" ]]; then
160
- error "指定的模板目录不存在: $TEMPLATE_DIR"
161
- fi
162
- if [[ ! -f "$TEMPLATE_DIR/package.json" ]]; then
163
- error "模板目录中没有 package.json: $TEMPLATE_DIR"
164
- fi
165
- return
166
- fi
167
-
168
- # 优先检查当前工作目录
169
- if [[ -f "$(pwd)/package.json" ]] && [[ -f "$(pwd)/src/index.tsx" ]]; then
170
- TEMPLATE_DIR="$(pwd)"
171
- return
172
- fi
173
-
174
- # 在模板根目录下查找 0.77 和 0.82
175
- for ver in "0.77" "0.82"; do
176
- local candidate="${TEMPLATE_ROOT}/${ver}"
177
- if [[ -d "$candidate" && -f "${candidate}/package.json" && -f "${candidate}/src/index.tsx" ]]; then
178
- TEMPLATE_DIR="$candidate"
179
- return
180
- fi
181
- done
182
-
183
- # 向上查找
184
- local search_dir
185
- search_dir="$(pwd)"
186
- for _i in $(seq 1 5); do
187
- if [[ -f "${search_dir}/package.json" && -f "${search_dir}/src/index.tsx" ]]; then
188
- TEMPLATE_DIR="$search_dir"
189
- return
190
- fi
191
- search_dir="$(dirname "$search_dir")"
192
- done
193
-
194
- error "未找到模板工程目录 (需要包含 package.json 和 src/index.tsx), 请使用 --template-dir 指定"
195
- }
196
-
197
- detect_template_dir
198
- info "模板工程目录: ${TEMPLATE_DIR}"
199
-
200
- # ════════════════════════════════════════════════════════════
201
- # 阶段 1: git clone 仓库到 TestGet/ 目录
202
- # ════════════════════════════════════════════════════════════
203
-
204
- echo ""
205
- echo "=========================================="
206
- echo " RNOH 测试 Demo/用例移植"
207
- echo "=========================================="
208
- echo ""
209
-
210
- # TestGet 目录位于脚本调用时的当前工作目录下
211
- WORK_DIR="$(pwd)/TestGet"
212
- info "工作目录: ${WORK_DIR}"
213
-
214
- # 从 URL 中提取仓库名作为克隆目录名
215
- REPO_NAME=$(basename "$GIT_URL" .git)
216
- debug "仓库名: ${REPO_NAME}"
217
-
218
- CLONE_DIR="${WORK_DIR}/${REPO_NAME}"
219
-
220
- info "克隆仓库: ${GIT_URL}"
221
- if [[ "$GIT_BRANCH" == "--" ]]; then
222
- info " 分支: (默认)"
223
- mkdir -p "$WORK_DIR"
224
- rm -rf "$CLONE_DIR"
225
- git clone --depth=1 "$GIT_URL" "$CLONE_DIR" 2>&1 | tail -3
226
- else
227
- info " 分支: ${GIT_BRANCH}"
228
- mkdir -p "$WORK_DIR"
229
- rm -rf "$CLONE_DIR"
230
- git clone --depth=1 -b "$GIT_BRANCH" "$GIT_URL" "$CLONE_DIR" 2>&1 | tail -3
231
- fi
232
-
233
- [[ -d "$CLONE_DIR" ]] || error "git clone 失败: $GIT_URL"
234
- [[ -d "$CLONE_DIR/.git" ]] || error "git clone 不完整: $CLONE_DIR"
235
-
236
- # 移除 .git 目录 (减少体积, 避免后续拷贝时出问题)
237
- rm -rf "$CLONE_DIR/.git"
238
-
239
- ok "仓库克隆完成: ${CLONE_DIR}"
240
- echo ""
241
-
242
- # ════════════════════════════════════════════════════════════
243
- # 阶段 2: 检测测试 demo/用例目录
244
- # ════════════════════════════════════════════════════════════
245
-
246
- info "检测测试 demo/用例目录..."
247
-
248
- # 检测 demo 目录
249
- # 支持手动指定或自动检测
250
- # 自动检测策略:
251
- # 1. 优先检查根目录下的已知 demo 目录名
252
- # 2. 如果根目录没有, 在 packages/*/ 下查找 (monorepo 场景)
253
- DETECTED_DEMO_DIRS=()
254
-
255
- if [[ -n "$DEMO_DIRS_OVERRIDE" ]]; then
256
- # 用户手动指定
257
- IFS=',' read -ra DETECTED_DEMO_DIRS <<< "$DEMO_DIRS_OVERRIDE"
258
- info " 使用手动指定的 demo 目录: ${DETECTED_DEMO_DIRS[*]}"
259
- else
260
- # 自动检测: 检查根目录
261
- # 注意: macOS 文件系统大小写不敏感, example 和 Example 会匹配同一个目录
262
- # 需要用小写名去重, 但保留实际目录名
263
- # 不使用关联数组 (macOS bash 3.2 不支持 declare -A)
264
- SEEN_DIRS_LOWER=""
265
- for dir in "${KNOWN_DEMO_DIRS[@]}"; do
266
- if [[ -d "${CLONE_DIR}/${dir}" ]]; then
267
- # 获取实际目录名 (解决大小写不敏感问题)
268
- # ls 输出的目录名是磁盘上的实际大小写
269
- actual_dir=$(ls -1 "${CLONE_DIR}" | grep -ix "^${dir}\$" 2>/dev/null | head -1)
270
- [[ -z "$actual_dir" ]] && actual_dir="$dir"
271
- dir_lower=$(echo "$actual_dir" | tr '[:upper:]' '[:lower:]')
272
- # 检查是否已存在 (用空格分隔的字符串)
273
- if [[ -z "$(echo " $SEEN_DIRS_LOWER " | grep " $dir_lower ")" ]]; then
274
- SEEN_DIRS_LOWER="${SEEN_DIRS_LOWER} ${dir_lower}"
275
- DETECTED_DEMO_DIRS+=("$actual_dir")
276
- debug " 根目录发现: ${actual_dir}/"
277
- fi
278
- fi
279
- done
280
- unset SEEN_DIRS_LOWER
281
-
282
- # 自动检测: 检查 monorepo 子包中的 demo 目录
283
- if [[ ${#DETECTED_DEMO_DIRS[@]} -eq 0 ]] && [[ -d "${CLONE_DIR}/packages" ]]; then
284
- debug " 根目录未发现 demo 目录, 检查 monorepo packages/*/"
285
- SEEN_MONO_LOWER=""
286
- for pkg_subdir in "${CLONE_DIR}"/packages/*/; do
287
- [[ -d "$pkg_subdir" ]] || continue
288
- pkg_rel="${pkg_subdir#${CLONE_DIR}/}"
289
- pkg_rel="${pkg_rel%/}"
290
- # 获取子包内实际目录名
291
- for dir in "${KNOWN_DEMO_DIRS[@]}"; do
292
- if [[ -d "${pkg_subdir}${dir}" ]]; then
293
- actual_mono_dir=$(ls -1 "${pkg_subdir}" | grep -ix "^${dir}\$" 2>/dev/null | head -1)
294
- [[ -z "$actual_mono_dir" ]] && actual_mono_dir="$dir"
295
- full_path="${pkg_rel}/${actual_mono_dir}"
296
- full_lower=$(echo "$full_path" | tr '[:upper:]' '[:lower:]')
297
- if [[ -z "$(echo " $SEEN_MONO_LOWER " | grep " $full_lower ")" ]]; then
298
- SEEN_MONO_LOWER="${SEEN_MONO_LOWER} ${full_lower}"
299
- DETECTED_DEMO_DIRS+=("$full_path")
300
- debug " monorepo 子包发现: ${full_path}/"
301
- fi
302
- fi
303
- done
304
- done
305
- unset SEEN_MONO_LOWER
306
- fi
307
- fi
308
-
309
- if [[ ${#DETECTED_DEMO_DIRS[@]} -eq 0 ]]; then
310
- warn "未检测到任何测试 demo/用例目录"
311
- warn "已知目录名: ${KNOWN_DEMO_DIRS[*]}"
312
- warn "可使用 --demo-dirs 手动指定"
313
- echo ""
314
-
315
- # 列出仓库根目录内容供用户参考
316
- info "仓库根目录内容:"
317
- ls -1 "$CLONE_DIR" | head -20
318
- exit 0
319
- fi
320
-
321
- echo ""
322
- echo "──────────────────────────────────────────"
323
- echo " 检测到 ${#DETECTED_DEMO_DIRS[@]} 个 demo/用例目录"
324
- echo "──────────────────────────────────────────"
325
- for dir in "${DETECTED_DEMO_DIRS[@]}"; do
326
- echo -e " ${GREEN}✓${NC} ${dir}/"
327
- done
328
- echo ""
329
-
330
- if [[ "$DRY_RUN" == "true" ]]; then
331
- info "dry-run 模式: 不执行实际拷贝"
332
-
333
- # 展示各目录内容
334
- for dir in "${DETECTED_DEMO_DIRS[@]}"; do
335
- echo ""
336
- info "目录 ${dir}/ 内容:"
337
- ls -1 "${CLONE_DIR}/${dir}" 2>/dev/null | head -15 || true
338
- done
339
-
340
- # 展示 package.json 依赖
341
- for dir in "${DETECTED_DEMO_DIRS[@]}"; do
342
- local_pkg="${CLONE_DIR}/${dir}/package.json"
343
- if [[ -f "$local_pkg" ]]; then
344
- echo ""
345
- info "${dir}/package.json 依赖:"
346
- python3 -c "
347
- import json, sys
348
- with open(sys.argv[1]) as f:
349
- data = json.load(f)
350
- deps = data.get('dependencies', {})
351
- dev_deps = data.get('devDependencies', {})
352
- if deps:
353
- print(' dependencies:')
354
- for k, v in deps.items():
355
- print(f' {k}: {v}')
356
- if dev_deps:
357
- print(' devDependencies:')
358
- for k, v in dev_deps.items():
359
- print(f' {k}: {v}')
360
- if not deps and not dev_deps:
361
- print(' (无依赖)')
362
- " "$local_pkg" 2>/dev/null || warn " 无法解析 package.json"
363
- fi
364
- done
365
-
366
- echo ""
367
- ok "dry-run 完成"
368
- exit 0
369
- fi
370
-
371
- # ════════════════════════════════════════════════════════════
372
- # 阶段 3: 安装 demo 依赖到模板工程 (本地依赖)
373
- # ════════════════════════════════════════════════════════════
374
-
375
- # 收集所有 demo 目录中的 package.json 依赖
376
- # 依赖安装策略:
377
- # - 跳过 react / react-native (模板工程已有)
378
- # - 跳过 workspace: 协议依赖 (monorepo 内部引用, 需特殊处理)
379
- # - 其他依赖以 npm 包方式安装到模板工程
380
- # - workspace: 依赖指向的本仓库自身的库, 以本地依赖 (file:) 方式安装
381
- # 需要从仓库根的 package.json 获取真实包名和版本
382
-
383
- # 模板工程已有的依赖 (从 package.json 读取)
384
- TEMPLATE_DEPS_JSON=""
385
- TEMPLATE_DEPS_JSON=$(python3 -c "
386
- import json, sys
387
- with open(sys.argv[1]) as f:
388
- data = json.load(f)
389
- deps = data.get('dependencies', {})
390
- dev_deps = data.get('devDependencies', {})
391
- all_deps = set(list(deps.keys()) + list(dev_deps.keys()))
392
- for d in sorted(all_deps):
393
- print(d)
394
- " "$TEMPLATE_DIR/package.json" 2>/dev/null || echo "")
395
-
396
- # 读取仓库根 package.json (用于处理 workspace: 依赖)
397
- REPO_PKG_JSON="${CLONE_DIR}/package.json"
398
- REPO_NPM_NAME=""
399
- REPO_NPM_VERSION=""
400
- if [[ -f "$REPO_PKG_JSON" ]]; then
401
- REPO_NPM_NAME=$(python3 -c "
402
- import json, sys
403
- with open(sys.argv[1]) as f:
404
- data = json.load(f)
405
- print(data.get('name', ''))
406
- " "$REPO_PKG_JSON" 2>/dev/null || echo "")
407
- REPO_NPM_VERSION=$(python3 -c "
408
- import json, sys
409
- with open(sys.argv[1]) as f:
410
- data = json.load(f)
411
- print(data.get('version', '*'))
412
- " "$REPO_PKG_JSON" 2>/dev/null || echo "*")
413
- debug "仓库包名: ${REPO_NPM_NAME}, 版本: ${REPO_NPM_VERSION}"
414
- fi
415
-
416
- if [[ "$SKIP_NPM_INSTALL" == "true" ]]; then
417
- warn "跳过依赖安装步骤 (--skip-npm-install)"
418
- else
419
- info "开始安装 demo 依赖到模板工程..."
420
-
421
- # 收集需要安装的依赖
422
- # 格式: 包名|版本, 用临时文件传递给 python 处理
423
- DEPS_TMP_FILE=$(mktemp /tmp/port-test-demo-deps.XXXXXX)
424
- trap 'rm -f "$DEPS_TMP_FILE"' EXIT
425
-
426
- for dir in "${DETECTED_DEMO_DIRS[@]}"; do
427
- local_pkg="${CLONE_DIR}/${dir}/package.json"
428
- [[ -f "$local_pkg" ]] || continue
429
-
430
- info " 解析 ${dir}/package.json 依赖..."
431
-
432
- # 提取 dependencies 和 devDependencies
433
- # 输出格式: dep_name|dep_version (每行一个)
434
- python3 -c "
435
- import json, sys
436
-
437
- pkg_path = sys.argv[1]
438
- repo_name = sys.argv[2]
439
- repo_version = sys.argv[3]
440
-
441
- with open(pkg_path) as f:
442
- data = json.load(f)
443
-
444
- all_deps = {}
445
- all_deps.update(data.get('dependencies', {}))
446
- all_deps.update(data.get('devDependencies', {}))
447
-
448
- for dep_name, dep_version in all_deps.items():
449
- # workspace: 协议 → 指向仓库自身, 替换为仓库包名和版本
450
- if dep_version.startswith('workspace:'):
451
- # workspace:* 或 workspace:^ 等 → 使用仓库根 package.json 的版本
452
- if repo_name and repo_name == dep_name:
453
- # demo 引用的就是仓库自身的主包, 用 file: 本地依赖
454
- print(f'{dep_name}|file:{repo_version}')
455
- elif repo_name:
456
- # 可能引用的是仓库内的子包, 也用 file: 指向仓库根
457
- print(f'{dep_name}|file:{repo_version}')
458
- else:
459
- # 无法确定包名, 用 latest
460
- print(f'{dep_name}|latest')
461
- elif dep_version.startswith('file:') or dep_version.startswith('link:'):
462
- # 已经是本地依赖, 原样输出
463
- clean_ver = dep_version.replace('file:', '').replace('link:', '')
464
- print(f'{dep_name}|file:{clean_ver}')
465
- else:
466
- # 普通 npm 依赖, 原样输出版本号
467
- print(f'{dep_name}|{dep_version}')
468
- " "$local_pkg" "$REPO_NPM_NAME" "$REPO_NPM_VERSION" >> "$DEPS_TMP_FILE" 2>/dev/null || warn " 无法解析 ${dir}/package.json"
469
- done
470
-
471
- # 去重并过滤掉模板工程已有的依赖和不需要的依赖
472
- # 需要排除的包名列表 (模板工程已有或不需要安装)
473
- EXCLUDE_PKGS=("react" "react-native" "react-dom" "react-test-renderer"
474
- "@babel/core" "@babel/preset-env" "@babel/runtime" "@babel/preset-react"
475
- "@react-native-community/cli" "@react-native-community/cli-platform-android"
476
- "@react-native-community/cli-platform-ios" "@react-native-community/eslint-config"
477
- "@react-native/babel-preset" "@react-native/eslint-config"
478
- "@react-native/metro-config" "@react-native/typescript-config"
479
- "@react-native-oh/react-native-harmony" "@react-native-oh/react-native-harmony-cli"
480
- "@tsconfig/react-native" "@types/react" "@types/react-test-renderer"
481
- "@types/jest" "babel-jest" "eslint" "jest" "metro-react-native-babel-preset"
482
- "prettier" "typescript" "react-native-macos" "react-native-web"
483
- "react-native-test-app" "@rnx-kit/metro-config")
484
-
485
- # 构建排除模式字符串 (用于 grep -v -E)
486
- EXCLUDE_PATTERN=$(IFS='|' ; echo "${EXCLUDE_PKGS[*]}")
487
-
488
- # 去重 + 过滤
489
- UNIQUE_DEPS=$(sort -u "$DEPS_TMP_FILE" | grep -v -E "^(${EXCLUDE_PATTERN})\|" || true)
490
-
491
- if [[ -z "$UNIQUE_DEPS" ]]; then
492
- info " 没有需要安装的新依赖"
493
- else
494
- dep_count=$(echo "$UNIQUE_DEPS" | wc -l | tr -d ' ')
495
- info " 发现 ${dep_count} 个需要安装的依赖:"
496
- echo "$UNIQUE_DEPS" | while IFS='|' read -r dep_name dep_version; do
497
- echo -e " ${CYAN}${dep_name}${NC} -> ${dep_version}"
498
- done
499
- echo ""
500
-
501
- # 安装依赖
502
- info " 开始安装依赖..."
503
-
504
- # 分为两类: npm 包依赖 和 file: 本地依赖
505
- NPM_DEPS=()
506
- FILE_DEPS=()
507
-
508
- while IFS='|' read -r dep_name dep_version; do
509
- if [[ "$dep_version" == file:* ]]; then
510
- FILE_DEPS+=("${dep_name}@${dep_version}")
511
- else
512
- NPM_DEPS+=("${dep_name}@${dep_version}")
513
- fi
514
- done <<< "$UNIQUE_DEPS"
515
-
516
- # 安装 npm 包依赖
517
- if [[ ${#NPM_DEPS[@]} -gt 0 ]]; then
518
- info " 安装 npm 依赖 (${#NPM_DEPS[@]} 个)..."
519
- (cd "$TEMPLATE_DIR" && npm install --save "${NPM_DEPS[@]}" $NPM_INSTALL_FLAGS 2>&1 | tail -5) || warn "npm install 部分依赖可能安装失败"
520
- fi
521
-
522
- # 安装 file: 本地依赖
523
- # 对于 workspace: 依赖, file: 指向的路径是仓库根目录的版本号 (不是实际路径)
524
- # 需要将其改为指向 TestGet/ 下的实际仓库路径
525
- if [[ ${#FILE_DEPS[@]} -gt 0 ]]; then
526
- info " 安装本地依赖 (${#FILE_DEPS[@]} 个)..."
527
-
528
- for file_dep in "${FILE_DEPS[@]}"; do
529
- dep_name="${file_dep%%@*}"
530
- dep_value="${file_dep#*@}"
531
-
532
- # 如果 file: 后面跟的是版本号 (来自 workspace: 转换), 改为指向实际仓库路径
533
- file_target="${dep_value#file:}"
534
- if [[ ! "$file_target" == /* && ! "$file_target" == ./* ]]; then
535
- # file_target 是版本号, 不是路径 → 指向 TestGet 中的仓库
536
- # 检查仓库中是否有 packages/<dep_name> 子目录 (monorepo)
537
- local_pkg_dir=""
538
- if [[ -d "${CLONE_DIR}/packages" ]]; then
539
- for pkg_subdir in "${CLONE_DIR}"/packages/*/; do
540
- if [[ -f "${pkg_subdir}package.json" ]]; then
541
- local_pkg_name=$(python3 -c "
542
- import json, sys
543
- with open(sys.argv[1]) as f:
544
- data = json.load(f)
545
- print(data.get('name', ''))
546
- " "${pkg_subdir}package.json" 2>/dev/null || echo "")
547
- if [[ "$local_pkg_name" == "$dep_name" ]]; then
548
- local_pkg_dir="${pkg_subdir%/}"
549
- break
550
- fi
551
- fi
552
- done
553
- fi
554
-
555
- # 如果没在 packages/ 中找到, 检查仓库根目录
556
- if [[ -z "$local_pkg_dir" && "$REPO_NPM_NAME" == "$dep_name" ]]; then
557
- local_pkg_dir="$CLONE_DIR"
558
- fi
559
-
560
- if [[ -n "$local_pkg_dir" ]]; then
561
- # 计算从模板工程到本地仓库的相对路径
562
- local_rel_path=$(python3 -c "
563
- import os, sys
564
- print(os.path.relpath(sys.argv[1], sys.argv[2]))
565
- " "$local_pkg_dir" "$TEMPLATE_DIR")
566
- dep_value="file:${local_rel_path}"
567
- else
568
- # 无法找到本地路径, 回退为普通 npm 安装
569
- warn " 无法找到 ${dep_name} 的本地路径, 尝试从 npm 安装"
570
- (cd "$TEMPLATE_DIR" && npm install --save "${dep_name}@latest" $NPM_INSTALL_FLAGS 2>&1 | tail -3) || warn " ${dep_name} 安装失败"
571
- continue
572
- fi
573
- fi
574
-
575
- info " ${dep_name} -> ${dep_value}"
576
- # 写入 package.json (file: 依赖)
577
- python3 -c "
578
- import json, sys
579
- pkg_path = sys.argv[1]
580
- dep_name = sys.argv[2]
581
- dep_value = sys.argv[3]
582
-
583
- with open(pkg_path) as f:
584
- pkg = json.load(f)
585
- deps = pkg.setdefault('dependencies', {})
586
- deps[dep_name] = dep_value
587
- with open(pkg_path, 'w') as f:
588
- json.dump(pkg, f, indent=2, ensure_ascii=False)
589
- f.write('\n')
590
- " "$TEMPLATE_DIR/package.json" "$dep_name" "$dep_value" || warn " 无法写入 ${dep_name} 到 package.json"
591
- done
592
-
593
- # 运行 npm install 解析 file: 依赖
594
- info " npm install (解析本地依赖)"
595
- (cd "$TEMPLATE_DIR" && npm install $NPM_INSTALL_FLAGS 2>&1 | tail -5) || warn "npm install 可能部分失败 (可忽略 peer dependency 警告)"
596
- fi
597
- fi
598
-
599
- ok "依赖安装完成"
600
- fi
601
-
602
- echo ""
603
-
604
- # ════════════════════════════════════════════════════════════
605
- # 阶段 4: 拷贝 demo/用例文件到模板工程
606
- # ════════════════════════════════════════════════════════════
607
-
608
- info "开始拷贝 demo/用例文件到模板工程..."
609
-
610
- # 模板工程 src/ 目录
611
- TEMPLATE_SRC_DIR="${TEMPLATE_DIR}/src"
612
-
613
- # demo 文件拷贝策略:
614
- # - demo 的支撑文件 (assets、animations、其他模块) 拷贝到 src/ 顶层
615
- # 这样 demo 代码中的 require("./animations/...") 等相对路径可以直接工作
616
- # - demo 的入口文件 (App.tsx / index.tsx 等) 不拷贝, 而是直接合并到 src/index.tsx
617
- # - 配置文件 (package.json / app.json / babel.config.js 等) 不拷贝
618
-
619
- # 入口文件名 (按优先级排序)
620
- ENTRY_FILE_NAMES=("App.tsx" "App.js" "index.tsx" "index.js")
621
-
622
- # 需要跳过的文件 (不拷贝到 src/ 顶层)
623
- # - 入口文件 → 直接合并到 index.tsx
624
- # - index.js (AppRegistry 注册) → 模板工程有自己的 index.js
625
- # - 配置文件 → 不拷贝
626
- SKIP_FILES=(
627
- "App.tsx"
628
- "App.js"
629
- "index.tsx"
630
- "index.js"
631
- "index.web.js"
632
- "app.json"
633
- "babel.config.js"
634
- "metro.config.js"
635
- "react-native.config.js"
636
- "webpack.config.js"
637
- ".watchmanconfig"
638
- ".gitignore"
639
- "tsconfig.json"
640
- "package.json"
641
- "Gemfile"
642
- "Gemfile.lock"
643
- "ReactTestApp-Resources.podspec.json"
644
- )
645
-
646
- # 需要排除的目录 (find -not -path)
647
- EXCLUDE_DIR_PATTERNS=(
648
- '*/node_modules/*'
649
- '*/android/*'
650
- '*/ios/*'
651
- '*/macos/*'
652
- '*/visionos/*'
653
- '*/dist/*'
654
- '*/build/*'
655
- '*/.git/*'
656
- '*/Pods/*'
657
- '*/public/*'
658
- )
659
-
660
- # 记录入口文件信息 (用于后续合并)
661
- # 格式: "demo_dir_name|entry_file_name"
662
- ENTRY_FILES=()
663
-
664
- for dir in "${DETECTED_DEMO_DIRS[@]}"; do
665
- src_dir="${CLONE_DIR}/${dir}"
666
- [[ -d "$src_dir" ]] || continue
667
-
668
- dir_name=$(basename "$dir")
669
-
670
- info " 处理 ${dir}/"
671
-
672
- # 检测入口文件 (在拷贝之前, 从源目录检测)
673
- found_entry=""
674
- for entry_file in "${ENTRY_FILE_NAMES[@]}"; do
675
- if [[ -f "${src_dir}/${entry_file}" ]]; then
676
- found_entry="$entry_file"
677
- debug " 发现入口文件: ${dir_name}/${entry_file}"
678
- break
679
- fi
680
- done
681
-
682
- if [[ -n "$found_entry" ]]; then
683
- ENTRY_FILES+=("${dir_name}|${found_entry}")
684
- fi
685
-
686
- # 构建 find 排除参数
687
- find_exclude_args=()
688
- for pattern in "${EXCLUDE_DIR_PATTERNS[@]}"; do
689
- find_exclude_args+=(-not -path "$pattern")
690
- done
691
-
692
- # 拷贝支撑文件 (assets、animations、其他模块) 到 src/ 顶层
693
- # 跳过入口文件和配置文件
694
- while IFS= read -r -d '' file; do
695
- # 计算相对于 demo 目录的相对路径
696
- rel_path="${file#${src_dir}/}"
697
- file_name=$(basename "$rel_path")
698
-
699
- # 跳过配置文件和入口文件
700
- skip=false
701
- for skip_file in "${SKIP_FILES[@]}"; do
702
- if [[ "$file_name" == "$skip_file" ]]; then
703
- skip=true
704
- break
705
- fi
706
- done
707
- [[ "$skip" == "true" ]] && { debug " 跳过: ${rel_path}"; continue; }
708
-
709
- # 目标路径: src/<rel_path>
710
- dest_file="${TEMPLATE_SRC_DIR}/${rel_path}"
711
- dest_parent=$(dirname "$dest_file")
712
- mkdir -p "$dest_parent"
713
- # 如果目标文件已存在, 跳过 (保留本地已适配的文件, 只拷贝缺失的文件)
714
- if [[ -f "$dest_file" ]]; then
715
- info " 已存在, 跳过: ${rel_path}"
716
- continue
717
- fi
718
- cp "$file" "$dest_file"
719
- debug " 拷贝: ${rel_path}"
720
- done < <(find "$src_dir" -type f "${find_exclude_args[@]}" -print0)
721
-
722
- done
723
-
724
- # 统计拷贝的文件数
725
- copied_count=$(find "$TEMPLATE_SRC_DIR" -type f -not -name 'index.tsx' -not -name 'index.backup.tsx' -not -name '*.d.ts' | wc -l | tr -d ' ')
726
- info " src/ 下共 ${copied_count} 个支撑文件"
727
-
728
- ok "文件拷贝完成"
729
- echo ""
730
-
731
- # ════════════════════════════════════════════════════════════
732
- # 阶段 5: 合并入口文件到 src/index.tsx
733
- # ════════════════════════════════════════════════════════════
734
-
735
- if [[ "$SKIP_INDEX_MERGE" == "true" ]]; then
736
- warn "跳过 index.tsx 合并步骤 (--skip-index-merge)"
737
- echo ""
738
- ok "测试 Demo/用例移植完成!"
739
- echo ""
740
- info "后续步骤:"
741
- echo " 1. 手动修改 src/index.tsx 引入 demo 组件"
742
- echo " 2. cd ${TEMPLATE_DIR} && npm run codegen"
743
- echo " 3. npm run dev"
744
- echo " 4. 用 DevEco Studio 打开 harmony/ 目录构建 HAP"
745
- exit 0
746
- fi
747
-
748
- info "合并入口文件到 src/index.tsx..."
749
-
750
- # 备份原始 index.tsx
751
- BACKUP_INDEX="${TEMPLATE_SRC_DIR}/index.backup.tsx"
752
- # 首次运行: 备份原始模板 index.tsx
753
- if [[ ! -f "$BACKUP_INDEX" ]]; then
754
- cp "${TEMPLATE_SRC_DIR}/index.tsx" "$BACKUP_INDEX"
755
- info " 已备份原始 index.tsx → index.backup.tsx"
756
- fi
757
- # 二次运行保护: 如果 index.tsx 已被修改 (与原始备份不同), 先备份当前版本再覆盖
758
- if [[ -f "$BACKUP_INDEX" ]] && ! cmp -s "${TEMPLATE_SRC_DIR}/index.tsx" "$BACKUP_INDEX"; then
759
- backup_ts=$(date +%s)
760
- cp "${TEMPLATE_SRC_DIR}/index.tsx" "${TEMPLATE_SRC_DIR}/index.tsx.bak.${backup_ts}"
761
- warn " index.tsx 已被修改 (可能已移植过 demo), 已备份当前版本 → index.tsx.bak.${backup_ts}"
762
- fi
763
-
764
- if [[ ${#ENTRY_FILES[@]} -eq 0 ]]; then
765
- warn "未检测到 demo 入口文件 (App.tsx / App.js / index.tsx / index.js)"
766
- warn "请手动修改 src/index.tsx 引入 demo 组件"
767
- else
768
- info " 检测到 ${#ENTRY_FILES[@]} 个入口文件:"
769
- for entry in "${ENTRY_FILES[@]}"; do
770
- # entry 格式: "demo_dir_name|entry_file_name"
771
- entry_dir_name="${entry%%|*}"
772
- entry_file_name="${entry##*|}"
773
- echo -e " ${GREEN}✓${NC} ${entry_dir_name}/${entry_file_name}"
774
- done
775
- echo ""
776
-
777
- # 生成新的 index.tsx
778
- # 策略:
779
- # - 如果只有一个入口文件, 将其内容直接写入 src/index.tsx
780
- # (去掉 AppRegistry 注册代码, 保留版权声明)
781
- # - 如果有多个入口文件, 拷贝各入口到 src/<demo_dir>/ 下, 生成 TabView 切换器
782
- # (切换器 import 各 demo 组件)
783
-
784
- if [[ ${#ENTRY_FILES[@]} -eq 1 ]]; then
785
- # ═══ 单个入口文件: 直接将内容写入 index.tsx ═══
786
- entry="${ENTRY_FILES[0]}"
787
- entry_dir_name="${entry%%|*}"
788
- entry_file_name="${entry##*|}"
789
-
790
- # 入口文件在源仓库中的路径
791
- # 如果 demo 目录在 monorepo 子包中 (如 packages/foo/example),
792
- # entry_dir_name 只是 basename, 需要找到完整的 demo 目录路径
793
- src_entry=""
794
- for dir in "${DETECTED_DEMO_DIRS[@]}"; do
795
- dir_bn=$(basename "$dir")
796
- if [[ "$dir_bn" == "$entry_dir_name" ]]; then
797
- src_entry="${CLONE_DIR}/${dir}/${entry_file_name}"
798
- break
799
- fi
800
- done
801
- [[ -z "$src_entry" || ! -f "$src_entry" ]] && src_entry="${CLONE_DIR}/${entry_dir_name}/${entry_file_name}"
802
- [[ ! -f "$src_entry" ]] && error "找不到入口文件: ${entry_dir_name}/${entry_file_name}"
803
-
804
- info " 将 ${entry_dir_name}/${entry_file_name} 内容写入 src/index.tsx"
805
-
806
- # 生成新的 index.tsx:
807
- # 1. 版权声明
808
- # 2. demo 入口文件内容 (去掉 AppRegistry 注册行)
809
- #
810
- # 过滤规则:
811
- # - 去掉包含 AppRegistry 的 import 行
812
- # - 去掉包含 AppRegistry.registerComponent 的行
813
- # - 去掉包含 appName 的 import (from './app.json')
814
- # - 去掉行首为 /** @format */ 的注释
815
-
816
- {
817
- # 版权声明
818
- cat <<'HEADER'
819
- /*
820
- * Copyright (c) 2025 Huawei Device Co., Ltd. All rights reserved
821
- * Use of this source code is governed by a MIT license that can be
822
- * found in the LICENSE file.
823
- */
824
-
825
- HEADER
826
-
827
- # demo 入口文件内容 (过滤 AppRegistry 相关代码)
828
- grep -vE 'AppRegistry|@format|from\s+["'\'']\./app\.json["'\'']' "$src_entry" || true
829
- } > "${TEMPLATE_SRC_DIR}/index.tsx"
830
-
831
- ok " 已更新 src/index.tsx ← ${entry_dir_name}/${entry_file_name} 内容"
832
-
833
- else
834
- # ═══ 多个入口文件: 拷贝各入口到 src/<demo_dir>/ 并生成切换器 ═══
835
- info " 生成多 demo 切换器组件..."
836
-
837
- # 拷贝各 demo 的入口文件到 src/<demo_dir>/ 下
838
- # 并收集导入信息
839
- IMPORTS=""
840
- DEMO_ITEMS=""
841
- idx=0
842
- for entry in "${ENTRY_FILES[@]}"; do
843
- entry_dir_name="${entry%%|*}"
844
- entry_file_name="${entry##*|}"
845
- entry_name="${entry_file_name%.*}" # 去掉扩展名
846
- component_name="Demo${idx}"
847
- demo_label="$entry_dir_name"
848
-
849
- # 找到源入口文件
850
- src_entry=""
851
- for dir in "${DETECTED_DEMO_DIRS[@]}"; do
852
- dir_bn=$(basename "$dir")
853
- if [[ "$dir_bn" == "$entry_dir_name" ]]; then
854
- src_entry="${CLONE_DIR}/${dir}/${entry_file_name}"
855
- break
856
- fi
857
- done
858
- [[ -z "$src_entry" || ! -f "$src_entry" ]] && src_entry="${CLONE_DIR}/${entry_dir_name}/${entry_file_name}"
859
- [[ ! -f "$src_entry" ]] && { warn "找不到入口文件: ${entry_dir_name}/${entry_file_name}, 跳过"; continue; }
860
-
861
- # 拷贝入口文件到 src/<demo_dir>/<entry_file>
862
- demo_dest_dir="${TEMPLATE_SRC_DIR}/${entry_dir_name}"
863
- mkdir -p "$demo_dest_dir"
864
- cp "$src_entry" "${demo_dest_dir}/${entry_file_name}"
865
-
866
- import_path="./${entry_dir_name}/${entry_name}"
867
-
868
- # 检测默认导出
869
- has_default_export=false
870
- if grep -qE 'export\s+default' "$src_entry" 2>/dev/null; then
871
- has_default_export=true
872
- fi
873
-
874
- if [[ "$has_default_export" == "true" ]]; then
875
- IMPORTS+="import ${component_name} from \"${import_path}\";\n"
876
- else
877
- export_name=$(grep -oE 'export\s+(const|function|class)\s+(\w+)' "$src_entry" 2>/dev/null | head -1 | awk '{print $3}')
878
- if [[ -n "$export_name" ]]; then
879
- IMPORTS+="import { ${export_name} as ${component_name} } from \"${import_path}\";\n"
880
- else
881
- IMPORTS+="import ${component_name} from \"${import_path}\";\n"
882
- fi
883
- fi
884
-
885
- DEMO_ITEMS+=" { key: \"${demo_label}\", component: ${component_name} },\n"
886
- ((idx++)) || true
887
- done
888
-
889
- # 生成切换器组件
890
- cat > "${TEMPLATE_SRC_DIR}/index.tsx" <<INDEX_EOF
891
- /*
892
- * Copyright (c) 2025 Huawei Device Co., Ltd. All rights reserved
893
- * Use of this source code is governed by a MIT license that can be
894
- * found in the LICENSE file.
895
- */
896
-
897
- import React, { useState } from "react";
898
- import { View, Text, TouchableOpacity, StyleSheet, ScrollView } from "react-native";
899
- $(printf "${IMPORTS}" | sed '/^$/d')
900
-
901
- interface DemoItem {
902
- key: string;
903
- component: React.ComponentType;
904
- }
905
-
906
- const demoItems: DemoItem[] = [
907
- $(printf "${DEMO_ITEMS}" | sed '/^$/d')
908
- ];
909
-
910
- const App = () => {
911
- const [currentDemo, setCurrentDemo] = useState(0);
912
-
913
- if (demoItems.length === 0) {
914
- return (
915
- <View style={styles.container}>
916
- <Text style={styles.emptyText}>No demo found</Text>
917
- </View>
918
- );
919
- }
920
-
921
- const CurrentComponent = demoItems[currentDemo].component;
922
-
923
- return (
924
- <View style={styles.container}>
925
- {/* Demo 切换栏 */}
926
- <View style={styles.tabBar}>
927
- <ScrollView horizontal showsHorizontalScrollIndicator={false}>
928
- {demoItems.map((item, index) => (
929
- <TouchableOpacity
930
- key={item.key}
931
- onPress={() => setCurrentDemo(index)}
932
- style={[
933
- styles.tab,
934
- currentDemo === index && styles.activeTab,
935
- ]}
936
- >
937
- <Text
938
- style={[
939
- styles.tabText,
940
- currentDemo === index && styles.activeTabText,
941
- ]}
942
- >
943
- {item.key}
944
- </Text>
945
- </TouchableOpacity>
946
- ))}
947
- </ScrollView>
948
- </View>
949
-
950
- {/* Demo 内容区 */}
951
- <View style={styles.content}>
952
- <CurrentComponent />
953
- </View>
954
- </View>
955
- );
956
- };
957
-
958
- const styles = StyleSheet.create({
959
- container: {
960
- flex: 1,
961
- backgroundColor: "#fff",
962
- },
963
- tabBar: {
964
- height: 50,
965
- backgroundColor: "#f5f5f5",
966
- borderBottomWidth: 1,
967
- borderBottomColor: "#e0e0e0",
968
- },
969
- tab: {
970
- paddingHorizontal: 16,
971
- height: 50,
972
- justifyContent: "center",
973
- alignItems: "center",
974
- },
975
- activeTab: {
976
- borderBottomWidth: 2,
977
- borderBottomColor: "#1652f0",
978
- },
979
- tabText: {
980
- fontSize: 14,
981
- color: "#666",
982
- },
983
- activeTabText: {
984
- color: "#1652f0",
985
- fontWeight: "600",
986
- },
987
- content: {
988
- flex: 1,
989
- },
990
- emptyText: {
991
- fontSize: 18,
992
- color: "#999",
993
- },
994
- });
995
-
996
- export default App;
997
- INDEX_EOF
998
-
999
- ok " 已更新 src/index.tsx → 多 demo 切换器 (${#ENTRY_FILES[@]} 个 demo)"
1000
- fi
1001
- fi
1002
-
1003
- echo ""
1004
-
1005
- # ════════════════════════════════════════════════════════════
1006
- # 阶段 6: 汇总输出
1007
- # ════════════════════════════════════════════════════════════
1008
-
1009
- echo "=========================================="
1010
- echo " 移植完成"
1011
- echo "=========================================="
1012
- echo ""
1013
-
1014
- info "汇总:"
1015
- echo " 仓库: ${GIT_URL} (${GIT_BRANCH})"
1016
- echo " 克隆到: ${CLONE_DIR}"
1017
- echo ""
1018
- echo " demo/用例目录 (${#DETECTED_DEMO_DIRS[@]} 个):"
1019
- for dir in "${DETECTED_DEMO_DIRS[@]}"; do
1020
- echo " ✓ ${dir}/"
1021
- done
1022
- echo ""
1023
- if [[ ${#ENTRY_FILES[@]} -gt 0 ]]; then
1024
- echo " 入口文件 (${#ENTRY_FILES[@]} 个):"
1025
- for entry in "${ENTRY_FILES[@]}"; do
1026
- entry_dir_name="${entry%%|*}"
1027
- entry_file_name="${entry##*|}"
1028
- if [[ ${#ENTRY_FILES[@]} -eq 1 ]]; then
1029
- echo " ✓ ${entry_dir_name}/${entry_file_name} → src/index.tsx (内容已写入)"
1030
- else
1031
- echo " ✓ ${entry_dir_name}/${entry_file_name} → src/${entry_dir_name}/${entry_file_name}"
1032
- fi
1033
- done
1034
- echo ""
1035
- fi
1036
- echo " 支撑文件 (assets/animations/模块等) 已拷贝到: src/"
1037
- echo " 模板工程: ${TEMPLATE_DIR}"
1038
- echo ""
1039
-
1040
- ok "测试 Demo/用例移植完成!"
1041
- echo ""
1042
- info "后续步骤:"
1043
- echo " 1. 检查 src/index.tsx 中的导入路径是否正确"
1044
- echo " 2. 检查 demo 中是否有平台特定代码需要适配 (如 Platform.OS === 'ios')"
1045
- echo " 3. cd ${TEMPLATE_DIR}"
1046
- echo " 4. npm run codegen"
1047
- echo " 5. npm run dev"
1048
- echo " 6. 用 DevEco Studio 打开 harmony/ 目录构建 HAP"
1049
- echo ""
1050
- warn "注意:"
1051
- echo " - demo 中的动画/图片等资源文件已拷贝到 src/ 下, 请确认路径引用正确"
1052
- echo " - 如果 demo 依赖了平台原生模块 (如 CameraPicker), 需要额外集成鸿蒙化版本"
1053
- echo " - 原始 index.tsx 已备份为 index.backup.tsx"
1
+ #!/usr/bin/env bash
2
+ #
3
+ # RNOH 测试 Demo/用例移植脚本
4
+ #
5
+ # 从 RN 三方库 git 仓库中拆解出测试 demo、用例目录,移植到模板工程中。
6
+ #
7
+ # 工作流程:
8
+ # 1. git clone 仓库到当前目录下的 TestGet/ 目录中
9
+ # 2. 自动检测仓库中的测试 demo/用例目录 (example、demo、test、tests、spec 等)
10
+ # 3. 如果 demo/用例目录中存在 package.json, 将其依赖的三方库安装到模板工程 (本地依赖)
11
+ # 4. 将 demo/用例目录中的所有文件按原有目录结构拷贝到模板工程 src/ 下
12
+ # 并将 demo 的入口组件接入到模板工程的 src/index.tsx
13
+ #
14
+ # 用法:
15
+ # ./scripts/port-test-demo.sh --git-url <url> --branch <branch> [选项]
16
+ #
17
+ # 示例:
18
+ # # 移植 lottie-react-native 的测试 demo
19
+ # ./scripts/port-test-demo.sh \
20
+ # --git-url https://github.com/lottie-react-native/lottie-react-native \
21
+ # --branch v7.4.0
22
+ #
23
+ # # 指定模板版本目录 (默认自动检测 0.77 或 0.82)
24
+ # ./scripts/port-test-demo.sh \
25
+ # --git-url https://github.com/lottie-react-native/lottie-react-native \
26
+ # --branch v7.4.0 \
27
+ # --template-dir /Users/lalhan/Desktop/RNOH/RN3rdTemplate/0.77
28
+ #
29
+ # # 手动指定 demo 目录 (跳过自动检测)
30
+ # ./scripts/port-test-demo.sh \
31
+ # --git-url https://github.com/lottie-react-native/lottie-react-native \
32
+ # --branch v7.4.0 \
33
+ # --demo-dirs example
34
+ #
35
+ # # dry-run 模式 (只检测不实际拷贝)
36
+ # ./scripts/port-test-demo.sh \
37
+ # --git-url https://github.com/lottie-react-native/lottie-react-native \
38
+ # --branch v7.4.0 \
39
+ # --dry-run
40
+ #
41
+
42
+ set -euo pipefail
43
+
44
+ # ── 颜色输出 ──────────────────────────────────────────────
45
+ RED='\033[0;31m'
46
+ GREEN='\033[0;32m'
47
+ YELLOW='\033[1;33m'
48
+ BLUE='\033[0;34m'
49
+ CYAN='\033[0;36m'
50
+ NC='\033[0m'
51
+
52
+ info() { echo -e "${BLUE}[INFO]${NC} $*"; }
53
+ ok() { echo -e "${GREEN}[OK]${NC} $*"; }
54
+ warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
55
+ error() { echo -e "${RED}[ERROR]${NC} $*" >&2; exit 1; }
56
+ debug() { [[ "${VERBOSE:-false}" == "true" ]] && echo -e "${CYAN}[DEBUG]${NC} $*" || true; }
57
+
58
+ # ── 默认值 ────────────────────────────────────────────────
59
+ GIT_URL=""
60
+ GIT_BRANCH=""
61
+ TEMPLATE_DIR=""
62
+ DEMO_DIRS_OVERRIDE=""
63
+ NPM_INSTALL_FLAGS=""
64
+ VERBOSE=false
65
+ DRY_RUN=false
66
+ SKIP_NPM_INSTALL=false
67
+ SKIP_INDEX_MERGE=false
68
+
69
+ # 已知的 demo/用例目录名 (按常见度排序)
70
+ KNOWN_DEMO_DIRS=("example" "demo" "Example" "Demo" "test" "tests" "Test" "Tests" "spec" "specs" "__tests__" "e2e" "cypress")
71
+
72
+ # 脚本所在目录 (scripts/)
73
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
74
+ # 模板根目录 (包含 0.77/ 0.82/ scripts/ 等)
75
+ TEMPLATE_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
76
+
77
+ # ── 参数解析 ──────────────────────────────────────────────
78
+ usage() {
79
+ cat <<'EOF'
80
+ RNOH 测试 Demo/用例移植脚本
81
+
82
+ 从 RN 三方库 git 仓库中拆解出测试 demo、用例目录,移植到模板工程中。
83
+
84
+ 用法:
85
+ ./scripts/port-test-demo.sh [选项]
86
+
87
+ 必选参数:
88
+ --git-url <url> git 仓库 URL (如 https://github.com/lottie-react-native/lottie-react-native)
89
+ --branch <branch> git 分支名 (如 v7.4.0)
90
+
91
+ 可选参数:
92
+ --template-dir <path> 模板工程目录 (如 /path/to/RN3rdTemplate/0.77), 不指定则自动检测
93
+ --demo-dirs <dir1,dir2,...> 手动指定 demo 目录名 (逗号分隔, 跳过自动检测)
94
+ --legacy-peer-deps npm install 时使用 --legacy-peer-deps
95
+ --force npm install 时使用 --force
96
+ --skip-npm-install 跳过依赖安装步骤
97
+ --skip-index-merge 跳过 index.tsx 合并步骤 (只拷贝文件不修改入口)
98
+ --verbose 显示详细调试信息
99
+ --dry-run 只检测不实际拷贝
100
+ --help, -h 显示帮助
101
+
102
+ 示例:
103
+ # 移植 lottie-react-native 的测试 demo
104
+ ./scripts/port-test-demo.sh \
105
+ --git-url https://github.com/lottie-react-native/lottie-react-native \
106
+ --branch v7.4.0
107
+
108
+ # 手动指定 demo 目录
109
+ ./scripts/port-test-demo.sh \
110
+ --git-url https://github.com/lottie-react-native/lottie-react-native \
111
+ --branch v7.4.0 \
112
+ --demo-dirs example
113
+
114
+ # dry-run 模式
115
+ ./scripts/port-test-demo.sh \
116
+ --git-url https://github.com/lottie-react-native/lottie-react-native \
117
+ --branch v7.4.0 \
118
+ --dry-run
119
+ EOF
120
+ exit 0
121
+ }
122
+
123
+ while [[ $# -gt 0 ]]; do
124
+ case "$1" in
125
+ --git-url) GIT_URL="$2"; shift 2;;
126
+ --branch) GIT_BRANCH="$2"; shift 2;;
127
+ --template-dir) TEMPLATE_DIR="$2"; shift 2;;
128
+ --demo-dirs) DEMO_DIRS_OVERRIDE="$2"; shift 2;;
129
+ --legacy-peer-deps) NPM_INSTALL_FLAGS="$NPM_INSTALL_FLAGS --legacy-peer-deps"; shift;;
130
+ --force) NPM_INSTALL_FLAGS="$NPM_INSTALL_FLAGS --force"; shift;;
131
+ --skip-npm-install) SKIP_NPM_INSTALL=true; shift;;
132
+ --skip-index-merge) SKIP_INDEX_MERGE=true; shift;;
133
+ --verbose) VERBOSE=true; shift;;
134
+ --dry-run) DRY_RUN=true; shift;;
135
+ --help|-h) usage;;
136
+ *) error "未知参数: $1 (使用 --help 查看用法)";;
137
+ esac
138
+ done
139
+
140
+ # ── 参数校验 ──────────────────────────────────────────────
141
+ if [[ -z "$GIT_URL" ]]; then
142
+ error "必须指定 --git-url 参数"
143
+ fi
144
+
145
+ if [[ -z "$GIT_BRANCH" ]]; then
146
+ warn "未指定 --branch, 将使用默认分支"
147
+ GIT_BRANCH="--"
148
+ fi
149
+
150
+ # ── 检查依赖 ──────────────────────────────────────────────
151
+ for cmd in python3 npm git rsync; do
152
+ command -v "$cmd" >/dev/null 2>&1 || error "未找到命令: $cmd, 请先安装"
153
+ done
154
+
155
+ # ── 自动检测模板工程目录 ──────────────────────────────────
156
+ detect_template_dir() {
157
+ # 如果用户指定了 --template-dir, 直接使用
158
+ if [[ -n "$TEMPLATE_DIR" ]]; then
159
+ if [[ ! -d "$TEMPLATE_DIR" ]]; then
160
+ error "指定的模板目录不存在: $TEMPLATE_DIR"
161
+ fi
162
+ if [[ ! -f "$TEMPLATE_DIR/package.json" ]]; then
163
+ error "模板目录中没有 package.json: $TEMPLATE_DIR"
164
+ fi
165
+ return
166
+ fi
167
+
168
+ # 优先检查当前工作目录
169
+ if [[ -f "$(pwd)/package.json" ]] && [[ -f "$(pwd)/src/index.tsx" ]]; then
170
+ TEMPLATE_DIR="$(pwd)"
171
+ return
172
+ fi
173
+
174
+ # 在模板根目录下查找 0.77 和 0.82
175
+ for ver in "0.77" "0.82"; do
176
+ local candidate="${TEMPLATE_ROOT}/${ver}"
177
+ if [[ -d "$candidate" && -f "${candidate}/package.json" && -f "${candidate}/src/index.tsx" ]]; then
178
+ TEMPLATE_DIR="$candidate"
179
+ return
180
+ fi
181
+ done
182
+
183
+ # 向上查找
184
+ local search_dir
185
+ search_dir="$(pwd)"
186
+ for _i in $(seq 1 5); do
187
+ if [[ -f "${search_dir}/package.json" && -f "${search_dir}/src/index.tsx" ]]; then
188
+ TEMPLATE_DIR="$search_dir"
189
+ return
190
+ fi
191
+ search_dir="$(dirname "$search_dir")"
192
+ done
193
+
194
+ error "未找到模板工程目录 (需要包含 package.json 和 src/index.tsx), 请使用 --template-dir 指定"
195
+ }
196
+
197
+ detect_template_dir
198
+ info "模板工程目录: ${TEMPLATE_DIR}"
199
+
200
+ # ════════════════════════════════════════════════════════════
201
+ # 阶段 1: git clone 仓库到 TestGet/ 目录
202
+ # ════════════════════════════════════════════════════════════
203
+
204
+ echo ""
205
+ echo "=========================================="
206
+ echo " RNOH 测试 Demo/用例移植"
207
+ echo "=========================================="
208
+ echo ""
209
+
210
+ # TestGet 目录位于脚本调用时的当前工作目录下
211
+ WORK_DIR="$(pwd)/TestGet"
212
+ info "工作目录: ${WORK_DIR}"
213
+
214
+ # 从 URL 中提取仓库名作为克隆目录名
215
+ REPO_NAME=$(basename "$GIT_URL" .git)
216
+ debug "仓库名: ${REPO_NAME}"
217
+
218
+ CLONE_DIR="${WORK_DIR}/${REPO_NAME}"
219
+
220
+ info "克隆仓库: ${GIT_URL}"
221
+ if [[ "$GIT_BRANCH" == "--" ]]; then
222
+ info " 分支: (默认)"
223
+ mkdir -p "$WORK_DIR"
224
+ rm -rf "$CLONE_DIR"
225
+ git clone --depth=1 "$GIT_URL" "$CLONE_DIR" 2>&1 | tail -3
226
+ else
227
+ info " 分支: ${GIT_BRANCH}"
228
+ mkdir -p "$WORK_DIR"
229
+ rm -rf "$CLONE_DIR"
230
+ git clone --depth=1 -b "$GIT_BRANCH" "$GIT_URL" "$CLONE_DIR" 2>&1 | tail -3
231
+ fi
232
+
233
+ [[ -d "$CLONE_DIR" ]] || error "git clone 失败: $GIT_URL"
234
+ [[ -d "$CLONE_DIR/.git" ]] || error "git clone 不完整: $CLONE_DIR"
235
+
236
+ # 移除 .git 目录 (减少体积, 避免后续拷贝时出问题)
237
+ rm -rf "$CLONE_DIR/.git"
238
+
239
+ ok "仓库克隆完成: ${CLONE_DIR}"
240
+ echo ""
241
+
242
+ # ════════════════════════════════════════════════════════════
243
+ # 阶段 2: 检测测试 demo/用例目录
244
+ # ════════════════════════════════════════════════════════════
245
+
246
+ info "检测测试 demo/用例目录..."
247
+
248
+ # 检测 demo 目录
249
+ # 支持手动指定或自动检测
250
+ # 自动检测策略:
251
+ # 1. 优先检查根目录下的已知 demo 目录名
252
+ # 2. 如果根目录没有, 在 packages/*/ 下查找 (monorepo 场景)
253
+ DETECTED_DEMO_DIRS=()
254
+
255
+ if [[ -n "$DEMO_DIRS_OVERRIDE" ]]; then
256
+ # 用户手动指定
257
+ IFS=',' read -ra DETECTED_DEMO_DIRS <<< "$DEMO_DIRS_OVERRIDE"
258
+ info " 使用手动指定的 demo 目录: ${DETECTED_DEMO_DIRS[*]}"
259
+ else
260
+ # 自动检测: 检查根目录
261
+ # 注意: macOS 文件系统大小写不敏感, example 和 Example 会匹配同一个目录
262
+ # 需要用小写名去重, 但保留实际目录名
263
+ # 不使用关联数组 (macOS bash 3.2 不支持 declare -A)
264
+ SEEN_DIRS_LOWER=""
265
+ for dir in "${KNOWN_DEMO_DIRS[@]}"; do
266
+ if [[ -d "${CLONE_DIR}/${dir}" ]]; then
267
+ # 获取实际目录名 (解决大小写不敏感问题)
268
+ # ls 输出的目录名是磁盘上的实际大小写
269
+ actual_dir=$(ls -1 "${CLONE_DIR}" | grep -ix "^${dir}\$" 2>/dev/null | head -1)
270
+ [[ -z "$actual_dir" ]] && actual_dir="$dir"
271
+ dir_lower=$(echo "$actual_dir" | tr '[:upper:]' '[:lower:]')
272
+ # 检查是否已存在 (用空格分隔的字符串)
273
+ if [[ -z "$(echo " $SEEN_DIRS_LOWER " | grep " $dir_lower ")" ]]; then
274
+ SEEN_DIRS_LOWER="${SEEN_DIRS_LOWER} ${dir_lower}"
275
+ DETECTED_DEMO_DIRS+=("$actual_dir")
276
+ debug " 根目录发现: ${actual_dir}/"
277
+ fi
278
+ fi
279
+ done
280
+ unset SEEN_DIRS_LOWER
281
+
282
+ # 自动检测: 检查 monorepo 子包中的 demo 目录
283
+ if [[ ${#DETECTED_DEMO_DIRS[@]} -eq 0 ]] && [[ -d "${CLONE_DIR}/packages" ]]; then
284
+ debug " 根目录未发现 demo 目录, 检查 monorepo packages/*/"
285
+ SEEN_MONO_LOWER=""
286
+ for pkg_subdir in "${CLONE_DIR}"/packages/*/; do
287
+ [[ -d "$pkg_subdir" ]] || continue
288
+ pkg_rel="${pkg_subdir#${CLONE_DIR}/}"
289
+ pkg_rel="${pkg_rel%/}"
290
+ # 获取子包内实际目录名
291
+ for dir in "${KNOWN_DEMO_DIRS[@]}"; do
292
+ if [[ -d "${pkg_subdir}${dir}" ]]; then
293
+ actual_mono_dir=$(ls -1 "${pkg_subdir}" | grep -ix "^${dir}\$" 2>/dev/null | head -1)
294
+ [[ -z "$actual_mono_dir" ]] && actual_mono_dir="$dir"
295
+ full_path="${pkg_rel}/${actual_mono_dir}"
296
+ full_lower=$(echo "$full_path" | tr '[:upper:]' '[:lower:]')
297
+ if [[ -z "$(echo " $SEEN_MONO_LOWER " | grep " $full_lower ")" ]]; then
298
+ SEEN_MONO_LOWER="${SEEN_MONO_LOWER} ${full_lower}"
299
+ DETECTED_DEMO_DIRS+=("$full_path")
300
+ debug " monorepo 子包发现: ${full_path}/"
301
+ fi
302
+ fi
303
+ done
304
+ done
305
+ unset SEEN_MONO_LOWER
306
+ fi
307
+ fi
308
+
309
+ if [[ ${#DETECTED_DEMO_DIRS[@]} -eq 0 ]]; then
310
+ warn "未检测到任何测试 demo/用例目录"
311
+ warn "已知目录名: ${KNOWN_DEMO_DIRS[*]}"
312
+ warn "可使用 --demo-dirs 手动指定"
313
+ echo ""
314
+
315
+ # 列出仓库根目录内容供用户参考
316
+ info "仓库根目录内容:"
317
+ ls -1 "$CLONE_DIR" | head -20
318
+ exit 0
319
+ fi
320
+
321
+ echo ""
322
+ echo "──────────────────────────────────────────"
323
+ echo " 检测到 ${#DETECTED_DEMO_DIRS[@]} 个 demo/用例目录"
324
+ echo "──────────────────────────────────────────"
325
+ for dir in "${DETECTED_DEMO_DIRS[@]}"; do
326
+ echo -e " ${GREEN}✓${NC} ${dir}/"
327
+ done
328
+ echo ""
329
+
330
+ if [[ "$DRY_RUN" == "true" ]]; then
331
+ info "dry-run 模式: 不执行实际拷贝"
332
+
333
+ # 展示各目录内容
334
+ for dir in "${DETECTED_DEMO_DIRS[@]}"; do
335
+ echo ""
336
+ info "目录 ${dir}/ 内容:"
337
+ ls -1 "${CLONE_DIR}/${dir}" 2>/dev/null | head -15 || true
338
+ done
339
+
340
+ # 展示 package.json 依赖
341
+ for dir in "${DETECTED_DEMO_DIRS[@]}"; do
342
+ local_pkg="${CLONE_DIR}/${dir}/package.json"
343
+ if [[ -f "$local_pkg" ]]; then
344
+ echo ""
345
+ info "${dir}/package.json 依赖:"
346
+ python3 -c "
347
+ import json, sys
348
+ with open(sys.argv[1]) as f:
349
+ data = json.load(f)
350
+ deps = data.get('dependencies', {})
351
+ dev_deps = data.get('devDependencies', {})
352
+ if deps:
353
+ print(' dependencies:')
354
+ for k, v in deps.items():
355
+ print(f' {k}: {v}')
356
+ if dev_deps:
357
+ print(' devDependencies:')
358
+ for k, v in dev_deps.items():
359
+ print(f' {k}: {v}')
360
+ if not deps and not dev_deps:
361
+ print(' (无依赖)')
362
+ " "$local_pkg" 2>/dev/null || warn " 无法解析 package.json"
363
+ fi
364
+ done
365
+
366
+ echo ""
367
+ ok "dry-run 完成"
368
+ exit 0
369
+ fi
370
+
371
+ # ════════════════════════════════════════════════════════════
372
+ # 阶段 3: 安装 demo 依赖到模板工程 (本地依赖)
373
+ # ════════════════════════════════════════════════════════════
374
+
375
+ # 收集所有 demo 目录中的 package.json 依赖
376
+ # 依赖安装策略:
377
+ # - 跳过 react / react-native (模板工程已有)
378
+ # - 跳过 workspace: 协议依赖 (monorepo 内部引用, 需特殊处理)
379
+ # - 其他依赖以 npm 包方式安装到模板工程
380
+ # - workspace: 依赖指向的本仓库自身的库, 以本地依赖 (file:) 方式安装
381
+ # 需要从仓库根的 package.json 获取真实包名和版本
382
+
383
+ # 模板工程已有的依赖 (从 package.json 读取)
384
+ TEMPLATE_DEPS_JSON=""
385
+ TEMPLATE_DEPS_JSON=$(python3 -c "
386
+ import json, sys
387
+ with open(sys.argv[1]) as f:
388
+ data = json.load(f)
389
+ deps = data.get('dependencies', {})
390
+ dev_deps = data.get('devDependencies', {})
391
+ all_deps = set(list(deps.keys()) + list(dev_deps.keys()))
392
+ for d in sorted(all_deps):
393
+ print(d)
394
+ " "$TEMPLATE_DIR/package.json" 2>/dev/null || echo "")
395
+
396
+ # 读取仓库根 package.json (用于处理 workspace: 依赖)
397
+ REPO_PKG_JSON="${CLONE_DIR}/package.json"
398
+ REPO_NPM_NAME=""
399
+ REPO_NPM_VERSION=""
400
+ if [[ -f "$REPO_PKG_JSON" ]]; then
401
+ REPO_NPM_NAME=$(python3 -c "
402
+ import json, sys
403
+ with open(sys.argv[1]) as f:
404
+ data = json.load(f)
405
+ print(data.get('name', ''))
406
+ " "$REPO_PKG_JSON" 2>/dev/null || echo "")
407
+ REPO_NPM_VERSION=$(python3 -c "
408
+ import json, sys
409
+ with open(sys.argv[1]) as f:
410
+ data = json.load(f)
411
+ print(data.get('version', '*'))
412
+ " "$REPO_PKG_JSON" 2>/dev/null || echo "*")
413
+ debug "仓库包名: ${REPO_NPM_NAME}, 版本: ${REPO_NPM_VERSION}"
414
+ fi
415
+
416
+ if [[ "$SKIP_NPM_INSTALL" == "true" ]]; then
417
+ warn "跳过依赖安装步骤 (--skip-npm-install)"
418
+ else
419
+ info "开始安装 demo 依赖到模板工程..."
420
+
421
+ # 收集需要安装的依赖
422
+ # 格式: 包名|版本, 用临时文件传递给 python 处理
423
+ DEPS_TMP_FILE=$(mktemp /tmp/port-test-demo-deps.XXXXXX)
424
+ trap 'rm -f "$DEPS_TMP_FILE"' EXIT
425
+
426
+ for dir in "${DETECTED_DEMO_DIRS[@]}"; do
427
+ local_pkg="${CLONE_DIR}/${dir}/package.json"
428
+ [[ -f "$local_pkg" ]] || continue
429
+
430
+ info " 解析 ${dir}/package.json 依赖..."
431
+
432
+ # 提取 dependencies 和 devDependencies
433
+ # 输出格式: dep_name|dep_version (每行一个)
434
+ python3 -c "
435
+ import json, sys
436
+
437
+ pkg_path = sys.argv[1]
438
+ repo_name = sys.argv[2]
439
+ repo_version = sys.argv[3]
440
+
441
+ with open(pkg_path) as f:
442
+ data = json.load(f)
443
+
444
+ all_deps = {}
445
+ all_deps.update(data.get('dependencies', {}))
446
+ all_deps.update(data.get('devDependencies', {}))
447
+
448
+ for dep_name, dep_version in all_deps.items():
449
+ # workspace: 协议 → 指向仓库自身, 替换为仓库包名和版本
450
+ if dep_version.startswith('workspace:'):
451
+ # workspace:* 或 workspace:^ 等 → 使用仓库根 package.json 的版本
452
+ if repo_name and repo_name == dep_name:
453
+ # demo 引用的就是仓库自身的主包, 用 file: 本地依赖
454
+ print(f'{dep_name}|file:{repo_version}')
455
+ elif repo_name:
456
+ # 可能引用的是仓库内的子包, 也用 file: 指向仓库根
457
+ print(f'{dep_name}|file:{repo_version}')
458
+ else:
459
+ # 无法确定包名, 用 latest
460
+ print(f'{dep_name}|latest')
461
+ elif dep_version.startswith('file:') or dep_version.startswith('link:'):
462
+ # 已经是本地依赖, 原样输出
463
+ clean_ver = dep_version.replace('file:', '').replace('link:', '')
464
+ print(f'{dep_name}|file:{clean_ver}')
465
+ else:
466
+ # 普通 npm 依赖, 原样输出版本号
467
+ print(f'{dep_name}|{dep_version}')
468
+ " "$local_pkg" "$REPO_NPM_NAME" "$REPO_NPM_VERSION" >> "$DEPS_TMP_FILE" 2>/dev/null || warn " 无法解析 ${dir}/package.json"
469
+ done
470
+
471
+ # 去重并过滤掉模板工程已有的依赖和不需要的依赖
472
+ # 需要排除的包名列表 (模板工程已有或不需要安装)
473
+ EXCLUDE_PKGS=("react" "react-native" "react-dom" "react-test-renderer"
474
+ "@babel/core" "@babel/preset-env" "@babel/runtime" "@babel/preset-react"
475
+ "@react-native-community/cli" "@react-native-community/cli-platform-android"
476
+ "@react-native-community/cli-platform-ios" "@react-native-community/eslint-config"
477
+ "@react-native/babel-preset" "@react-native/eslint-config"
478
+ "@react-native/metro-config" "@react-native/typescript-config"
479
+ "@react-native-oh/react-native-harmony" "@react-native-oh/react-native-harmony-cli"
480
+ "@tsconfig/react-native" "@types/react" "@types/react-test-renderer"
481
+ "@types/jest" "babel-jest" "eslint" "jest" "metro-react-native-babel-preset"
482
+ "prettier" "typescript" "react-native-macos" "react-native-web"
483
+ "react-native-test-app" "@rnx-kit/metro-config")
484
+
485
+ # 构建排除模式字符串 (用于 grep -v -E)
486
+ EXCLUDE_PATTERN=$(IFS='|' ; echo "${EXCLUDE_PKGS[*]}")
487
+
488
+ # 去重 + 过滤
489
+ UNIQUE_DEPS=$(sort -u "$DEPS_TMP_FILE" | grep -v -E "^(${EXCLUDE_PATTERN})\|" || true)
490
+
491
+ if [[ -z "$UNIQUE_DEPS" ]]; then
492
+ info " 没有需要安装的新依赖"
493
+ else
494
+ dep_count=$(echo "$UNIQUE_DEPS" | wc -l | tr -d ' ')
495
+ info " 发现 ${dep_count} 个需要安装的依赖:"
496
+ echo "$UNIQUE_DEPS" | while IFS='|' read -r dep_name dep_version; do
497
+ echo -e " ${CYAN}${dep_name}${NC} -> ${dep_version}"
498
+ done
499
+ echo ""
500
+
501
+ # 安装依赖
502
+ info " 开始安装依赖..."
503
+
504
+ # 分为两类: npm 包依赖 和 file: 本地依赖
505
+ NPM_DEPS=()
506
+ FILE_DEPS=()
507
+
508
+ while IFS='|' read -r dep_name dep_version; do
509
+ if [[ "$dep_version" == file:* ]]; then
510
+ FILE_DEPS+=("${dep_name}@${dep_version}")
511
+ else
512
+ NPM_DEPS+=("${dep_name}@${dep_version}")
513
+ fi
514
+ done <<< "$UNIQUE_DEPS"
515
+
516
+ # 安装 npm 包依赖
517
+ if [[ ${#NPM_DEPS[@]} -gt 0 ]]; then
518
+ info " 安装 npm 依赖 (${#NPM_DEPS[@]} 个)..."
519
+ (cd "$TEMPLATE_DIR" && npm install --save "${NPM_DEPS[@]}" $NPM_INSTALL_FLAGS 2>&1 | tail -5) || warn "npm install 部分依赖可能安装失败"
520
+ fi
521
+
522
+ # 安装 file: 本地依赖
523
+ # 对于 workspace: 依赖, file: 指向的路径是仓库根目录的版本号 (不是实际路径)
524
+ # 需要将其改为指向 TestGet/ 下的实际仓库路径
525
+ if [[ ${#FILE_DEPS[@]} -gt 0 ]]; then
526
+ info " 安装本地依赖 (${#FILE_DEPS[@]} 个)..."
527
+
528
+ for file_dep in "${FILE_DEPS[@]}"; do
529
+ dep_name="${file_dep%%@*}"
530
+ dep_value="${file_dep#*@}"
531
+
532
+ # 如果 file: 后面跟的是版本号 (来自 workspace: 转换), 改为指向实际仓库路径
533
+ file_target="${dep_value#file:}"
534
+ if [[ ! "$file_target" == /* && ! "$file_target" == ./* ]]; then
535
+ # file_target 是版本号, 不是路径 → 指向 TestGet 中的仓库
536
+ # 检查仓库中是否有 packages/<dep_name> 子目录 (monorepo)
537
+ local_pkg_dir=""
538
+ if [[ -d "${CLONE_DIR}/packages" ]]; then
539
+ for pkg_subdir in "${CLONE_DIR}"/packages/*/; do
540
+ if [[ -f "${pkg_subdir}package.json" ]]; then
541
+ local_pkg_name=$(python3 -c "
542
+ import json, sys
543
+ with open(sys.argv[1]) as f:
544
+ data = json.load(f)
545
+ print(data.get('name', ''))
546
+ " "${pkg_subdir}package.json" 2>/dev/null || echo "")
547
+ if [[ "$local_pkg_name" == "$dep_name" ]]; then
548
+ local_pkg_dir="${pkg_subdir%/}"
549
+ break
550
+ fi
551
+ fi
552
+ done
553
+ fi
554
+
555
+ # 如果没在 packages/ 中找到, 检查仓库根目录
556
+ if [[ -z "$local_pkg_dir" && "$REPO_NPM_NAME" == "$dep_name" ]]; then
557
+ local_pkg_dir="$CLONE_DIR"
558
+ fi
559
+
560
+ if [[ -n "$local_pkg_dir" ]]; then
561
+ # 计算从模板工程到本地仓库的相对路径
562
+ local_rel_path=$(python3 -c "
563
+ import os, sys
564
+ print(os.path.relpath(sys.argv[1], sys.argv[2]))
565
+ " "$local_pkg_dir" "$TEMPLATE_DIR")
566
+ dep_value="file:${local_rel_path}"
567
+ else
568
+ # 无法找到本地路径, 回退为普通 npm 安装
569
+ warn " 无法找到 ${dep_name} 的本地路径, 尝试从 npm 安装"
570
+ (cd "$TEMPLATE_DIR" && npm install --save "${dep_name}@latest" $NPM_INSTALL_FLAGS 2>&1 | tail -3) || warn " ${dep_name} 安装失败"
571
+ continue
572
+ fi
573
+ fi
574
+
575
+ info " ${dep_name} -> ${dep_value}"
576
+ # 写入 package.json (file: 依赖)
577
+ python3 -c "
578
+ import json, sys
579
+ pkg_path = sys.argv[1]
580
+ dep_name = sys.argv[2]
581
+ dep_value = sys.argv[3]
582
+
583
+ with open(pkg_path) as f:
584
+ pkg = json.load(f)
585
+ deps = pkg.setdefault('dependencies', {})
586
+ deps[dep_name] = dep_value
587
+ with open(pkg_path, 'w') as f:
588
+ json.dump(pkg, f, indent=2, ensure_ascii=False)
589
+ f.write('\n')
590
+ " "$TEMPLATE_DIR/package.json" "$dep_name" "$dep_value" || warn " 无法写入 ${dep_name} 到 package.json"
591
+ done
592
+
593
+ # 运行 npm install 解析 file: 依赖
594
+ info " npm install (解析本地依赖)"
595
+ (cd "$TEMPLATE_DIR" && npm install $NPM_INSTALL_FLAGS 2>&1 | tail -5) || warn "npm install 可能部分失败 (可忽略 peer dependency 警告)"
596
+ fi
597
+ fi
598
+
599
+ ok "依赖安装完成"
600
+ fi
601
+
602
+ echo ""
603
+
604
+ # ════════════════════════════════════════════════════════════
605
+ # 阶段 4: 拷贝 demo/用例文件到模板工程
606
+ # ════════════════════════════════════════════════════════════
607
+
608
+ info "开始拷贝 demo/用例文件到模板工程..."
609
+
610
+ # 模板工程 src/ 目录
611
+ TEMPLATE_SRC_DIR="${TEMPLATE_DIR}/src"
612
+
613
+ # demo 文件拷贝策略:
614
+ # - demo 的支撑文件 (assets、animations、其他模块) 拷贝到 src/ 顶层
615
+ # 这样 demo 代码中的 require("./animations/...") 等相对路径可以直接工作
616
+ # - demo 的入口文件 (App.tsx / index.tsx 等) 不拷贝, 而是直接合并到 src/index.tsx
617
+ # - 配置文件 (package.json / app.json / babel.config.js 等) 不拷贝
618
+
619
+ # 入口文件名 (按优先级排序)
620
+ ENTRY_FILE_NAMES=("App.tsx" "App.js" "index.tsx" "index.js")
621
+
622
+ # 需要跳过的文件 (不拷贝到 src/ 顶层)
623
+ # - 入口文件 → 直接合并到 index.tsx
624
+ # - index.js (AppRegistry 注册) → 模板工程有自己的 index.js
625
+ # - 配置文件 → 不拷贝
626
+ SKIP_FILES=(
627
+ "App.tsx"
628
+ "App.js"
629
+ "index.tsx"
630
+ "index.js"
631
+ "index.web.js"
632
+ "app.json"
633
+ "babel.config.js"
634
+ "metro.config.js"
635
+ "react-native.config.js"
636
+ "webpack.config.js"
637
+ ".watchmanconfig"
638
+ ".gitignore"
639
+ "tsconfig.json"
640
+ "package.json"
641
+ "Gemfile"
642
+ "Gemfile.lock"
643
+ "ReactTestApp-Resources.podspec.json"
644
+ )
645
+
646
+ # 需要排除的目录 (find -not -path)
647
+ EXCLUDE_DIR_PATTERNS=(
648
+ '*/node_modules/*'
649
+ '*/android/*'
650
+ '*/ios/*'
651
+ '*/macos/*'
652
+ '*/visionos/*'
653
+ '*/dist/*'
654
+ '*/build/*'
655
+ '*/.git/*'
656
+ '*/Pods/*'
657
+ '*/public/*'
658
+ )
659
+
660
+ # 记录入口文件信息 (用于后续合并)
661
+ # 格式: "demo_dir_name|entry_file_name"
662
+ ENTRY_FILES=()
663
+
664
+ for dir in "${DETECTED_DEMO_DIRS[@]}"; do
665
+ src_dir="${CLONE_DIR}/${dir}"
666
+ [[ -d "$src_dir" ]] || continue
667
+
668
+ dir_name=$(basename "$dir")
669
+
670
+ info " 处理 ${dir}/"
671
+
672
+ # 检测入口文件 (在拷贝之前, 从源目录检测)
673
+ found_entry=""
674
+ for entry_file in "${ENTRY_FILE_NAMES[@]}"; do
675
+ if [[ -f "${src_dir}/${entry_file}" ]]; then
676
+ found_entry="$entry_file"
677
+ debug " 发现入口文件: ${dir_name}/${entry_file}"
678
+ break
679
+ fi
680
+ done
681
+
682
+ if [[ -n "$found_entry" ]]; then
683
+ ENTRY_FILES+=("${dir_name}|${found_entry}")
684
+ fi
685
+
686
+ # 构建 find 排除参数
687
+ find_exclude_args=()
688
+ for pattern in "${EXCLUDE_DIR_PATTERNS[@]}"; do
689
+ find_exclude_args+=(-not -path "$pattern")
690
+ done
691
+
692
+ # 拷贝支撑文件 (assets、animations、其他模块) 到 src/ 顶层
693
+ # 跳过入口文件和配置文件
694
+ while IFS= read -r -d '' file; do
695
+ # 计算相对于 demo 目录的相对路径
696
+ rel_path="${file#${src_dir}/}"
697
+ file_name=$(basename "$rel_path")
698
+
699
+ # 跳过配置文件和入口文件
700
+ skip=false
701
+ for skip_file in "${SKIP_FILES[@]}"; do
702
+ if [[ "$file_name" == "$skip_file" ]]; then
703
+ skip=true
704
+ break
705
+ fi
706
+ done
707
+ [[ "$skip" == "true" ]] && { debug " 跳过: ${rel_path}"; continue; }
708
+
709
+ # 目标路径: src/<rel_path>
710
+ dest_file="${TEMPLATE_SRC_DIR}/${rel_path}"
711
+ dest_parent=$(dirname "$dest_file")
712
+ mkdir -p "$dest_parent"
713
+ # 如果目标文件已存在, 跳过 (保留本地已适配的文件, 只拷贝缺失的文件)
714
+ if [[ -f "$dest_file" ]]; then
715
+ info " 已存在, 跳过: ${rel_path}"
716
+ continue
717
+ fi
718
+ cp "$file" "$dest_file"
719
+ debug " 拷贝: ${rel_path}"
720
+ done < <(find "$src_dir" -type f "${find_exclude_args[@]}" -print0)
721
+
722
+ done
723
+
724
+ # 统计拷贝的文件数
725
+ copied_count=$(find "$TEMPLATE_SRC_DIR" -type f -not -name 'index.tsx' -not -name 'index.backup.tsx' -not -name '*.d.ts' | wc -l | tr -d ' ')
726
+ info " src/ 下共 ${copied_count} 个支撑文件"
727
+
728
+ ok "文件拷贝完成"
729
+ echo ""
730
+
731
+ # ════════════════════════════════════════════════════════════
732
+ # 阶段 5: 合并入口文件到 src/index.tsx
733
+ # ════════════════════════════════════════════════════════════
734
+
735
+ if [[ "$SKIP_INDEX_MERGE" == "true" ]]; then
736
+ warn "跳过 index.tsx 合并步骤 (--skip-index-merge)"
737
+ echo ""
738
+ ok "测试 Demo/用例移植完成!"
739
+ echo ""
740
+ info "后续步骤:"
741
+ echo " 1. 手动修改 src/index.tsx 引入 demo 组件"
742
+ echo " 2. cd ${TEMPLATE_DIR} && npm run codegen"
743
+ echo " 3. npm run dev"
744
+ echo " 4. 用 DevEco Studio 打开 harmony/ 目录构建 HAP"
745
+ exit 0
746
+ fi
747
+
748
+ info "合并入口文件到 src/index.tsx..."
749
+
750
+ # 备份原始 index.tsx
751
+ BACKUP_INDEX="${TEMPLATE_SRC_DIR}/index.backup.tsx"
752
+ # 首次运行: 备份原始模板 index.tsx
753
+ if [[ ! -f "$BACKUP_INDEX" ]]; then
754
+ cp "${TEMPLATE_SRC_DIR}/index.tsx" "$BACKUP_INDEX"
755
+ info " 已备份原始 index.tsx → index.backup.tsx"
756
+ fi
757
+ # 二次运行保护: 如果 index.tsx 已被修改 (与原始备份不同), 先备份当前版本再覆盖
758
+ if [[ -f "$BACKUP_INDEX" ]] && ! cmp -s "${TEMPLATE_SRC_DIR}/index.tsx" "$BACKUP_INDEX"; then
759
+ backup_ts=$(date +%s)
760
+ cp "${TEMPLATE_SRC_DIR}/index.tsx" "${TEMPLATE_SRC_DIR}/index.tsx.bak.${backup_ts}"
761
+ warn " index.tsx 已被修改 (可能已移植过 demo), 已备份当前版本 → index.tsx.bak.${backup_ts}"
762
+ fi
763
+
764
+ if [[ ${#ENTRY_FILES[@]} -eq 0 ]]; then
765
+ warn "未检测到 demo 入口文件 (App.tsx / App.js / index.tsx / index.js)"
766
+ warn "请手动修改 src/index.tsx 引入 demo 组件"
767
+ else
768
+ info " 检测到 ${#ENTRY_FILES[@]} 个入口文件:"
769
+ for entry in "${ENTRY_FILES[@]}"; do
770
+ # entry 格式: "demo_dir_name|entry_file_name"
771
+ entry_dir_name="${entry%%|*}"
772
+ entry_file_name="${entry##*|}"
773
+ echo -e " ${GREEN}✓${NC} ${entry_dir_name}/${entry_file_name}"
774
+ done
775
+ echo ""
776
+
777
+ # 生成新的 index.tsx
778
+ # 策略:
779
+ # - 如果只有一个入口文件, 将其内容直接写入 src/index.tsx
780
+ # (去掉 AppRegistry 注册代码, 保留版权声明)
781
+ # - 如果有多个入口文件, 拷贝各入口到 src/<demo_dir>/ 下, 生成 TabView 切换器
782
+ # (切换器 import 各 demo 组件)
783
+
784
+ if [[ ${#ENTRY_FILES[@]} -eq 1 ]]; then
785
+ # ═══ 单个入口文件: 直接将内容写入 index.tsx ═══
786
+ entry="${ENTRY_FILES[0]}"
787
+ entry_dir_name="${entry%%|*}"
788
+ entry_file_name="${entry##*|}"
789
+
790
+ # 入口文件在源仓库中的路径
791
+ # 如果 demo 目录在 monorepo 子包中 (如 packages/foo/example),
792
+ # entry_dir_name 只是 basename, 需要找到完整的 demo 目录路径
793
+ src_entry=""
794
+ for dir in "${DETECTED_DEMO_DIRS[@]}"; do
795
+ dir_bn=$(basename "$dir")
796
+ if [[ "$dir_bn" == "$entry_dir_name" ]]; then
797
+ src_entry="${CLONE_DIR}/${dir}/${entry_file_name}"
798
+ break
799
+ fi
800
+ done
801
+ [[ -z "$src_entry" || ! -f "$src_entry" ]] && src_entry="${CLONE_DIR}/${entry_dir_name}/${entry_file_name}"
802
+ [[ ! -f "$src_entry" ]] && error "找不到入口文件: ${entry_dir_name}/${entry_file_name}"
803
+
804
+ info " 将 ${entry_dir_name}/${entry_file_name} 内容写入 src/index.tsx"
805
+
806
+ # 生成新的 index.tsx:
807
+ # 1. 版权声明
808
+ # 2. demo 入口文件内容 (去掉 AppRegistry 注册行)
809
+ #
810
+ # 过滤规则:
811
+ # - 去掉包含 AppRegistry 的 import 行
812
+ # - 去掉包含 AppRegistry.registerComponent 的行
813
+ # - 去掉包含 appName 的 import (from './app.json')
814
+ # - 去掉行首为 /** @format */ 的注释
815
+
816
+ {
817
+ # 版权声明
818
+ cat <<'HEADER'
819
+ /*
820
+ * Copyright (c) 2025 Huawei Device Co., Ltd. All rights reserved
821
+ * Use of this source code is governed by a MIT license that can be
822
+ * found in the LICENSE file.
823
+ */
824
+
825
+ HEADER
826
+
827
+ # demo 入口文件内容 (过滤 AppRegistry 相关代码)
828
+ grep -vE 'AppRegistry|@format|from\s+["'\'']\./app\.json["'\'']' "$src_entry" || true
829
+ } > "${TEMPLATE_SRC_DIR}/index.tsx"
830
+
831
+ ok " 已更新 src/index.tsx ← ${entry_dir_name}/${entry_file_name} 内容"
832
+
833
+ else
834
+ # ═══ 多个入口文件: 拷贝各入口到 src/<demo_dir>/ 并生成切换器 ═══
835
+ info " 生成多 demo 切换器组件..."
836
+
837
+ # 拷贝各 demo 的入口文件到 src/<demo_dir>/ 下
838
+ # 并收集导入信息
839
+ IMPORTS=""
840
+ DEMO_ITEMS=""
841
+ idx=0
842
+ for entry in "${ENTRY_FILES[@]}"; do
843
+ entry_dir_name="${entry%%|*}"
844
+ entry_file_name="${entry##*|}"
845
+ entry_name="${entry_file_name%.*}" # 去掉扩展名
846
+ component_name="Demo${idx}"
847
+ demo_label="$entry_dir_name"
848
+
849
+ # 找到源入口文件
850
+ src_entry=""
851
+ for dir in "${DETECTED_DEMO_DIRS[@]}"; do
852
+ dir_bn=$(basename "$dir")
853
+ if [[ "$dir_bn" == "$entry_dir_name" ]]; then
854
+ src_entry="${CLONE_DIR}/${dir}/${entry_file_name}"
855
+ break
856
+ fi
857
+ done
858
+ [[ -z "$src_entry" || ! -f "$src_entry" ]] && src_entry="${CLONE_DIR}/${entry_dir_name}/${entry_file_name}"
859
+ [[ ! -f "$src_entry" ]] && { warn "找不到入口文件: ${entry_dir_name}/${entry_file_name}, 跳过"; continue; }
860
+
861
+ # 拷贝入口文件到 src/<demo_dir>/<entry_file>
862
+ demo_dest_dir="${TEMPLATE_SRC_DIR}/${entry_dir_name}"
863
+ mkdir -p "$demo_dest_dir"
864
+ cp "$src_entry" "${demo_dest_dir}/${entry_file_name}"
865
+
866
+ import_path="./${entry_dir_name}/${entry_name}"
867
+
868
+ # 检测默认导出
869
+ has_default_export=false
870
+ if grep -qE 'export\s+default' "$src_entry" 2>/dev/null; then
871
+ has_default_export=true
872
+ fi
873
+
874
+ if [[ "$has_default_export" == "true" ]]; then
875
+ IMPORTS+="import ${component_name} from \"${import_path}\";\n"
876
+ else
877
+ export_name=$(grep -oE 'export\s+(const|function|class)\s+(\w+)' "$src_entry" 2>/dev/null | head -1 | awk '{print $3}')
878
+ if [[ -n "$export_name" ]]; then
879
+ IMPORTS+="import { ${export_name} as ${component_name} } from \"${import_path}\";\n"
880
+ else
881
+ IMPORTS+="import ${component_name} from \"${import_path}\";\n"
882
+ fi
883
+ fi
884
+
885
+ DEMO_ITEMS+=" { key: \"${demo_label}\", component: ${component_name} },\n"
886
+ ((idx++)) || true
887
+ done
888
+
889
+ # 生成切换器组件
890
+ cat > "${TEMPLATE_SRC_DIR}/index.tsx" <<INDEX_EOF
891
+ /*
892
+ * Copyright (c) 2025 Huawei Device Co., Ltd. All rights reserved
893
+ * Use of this source code is governed by a MIT license that can be
894
+ * found in the LICENSE file.
895
+ */
896
+
897
+ import React, { useState } from "react";
898
+ import { View, Text, TouchableOpacity, StyleSheet, ScrollView } from "react-native";
899
+ $(printf "${IMPORTS}" | sed '/^$/d')
900
+
901
+ interface DemoItem {
902
+ key: string;
903
+ component: React.ComponentType;
904
+ }
905
+
906
+ const demoItems: DemoItem[] = [
907
+ $(printf "${DEMO_ITEMS}" | sed '/^$/d')
908
+ ];
909
+
910
+ const App = () => {
911
+ const [currentDemo, setCurrentDemo] = useState(0);
912
+
913
+ if (demoItems.length === 0) {
914
+ return (
915
+ <View style={styles.container}>
916
+ <Text style={styles.emptyText}>No demo found</Text>
917
+ </View>
918
+ );
919
+ }
920
+
921
+ const CurrentComponent = demoItems[currentDemo].component;
922
+
923
+ return (
924
+ <View style={styles.container}>
925
+ {/* Demo 切换栏 */}
926
+ <View style={styles.tabBar}>
927
+ <ScrollView horizontal showsHorizontalScrollIndicator={false}>
928
+ {demoItems.map((item, index) => (
929
+ <TouchableOpacity
930
+ key={item.key}
931
+ onPress={() => setCurrentDemo(index)}
932
+ style={[
933
+ styles.tab,
934
+ currentDemo === index && styles.activeTab,
935
+ ]}
936
+ >
937
+ <Text
938
+ style={[
939
+ styles.tabText,
940
+ currentDemo === index && styles.activeTabText,
941
+ ]}
942
+ >
943
+ {item.key}
944
+ </Text>
945
+ </TouchableOpacity>
946
+ ))}
947
+ </ScrollView>
948
+ </View>
949
+
950
+ {/* Demo 内容区 */}
951
+ <View style={styles.content}>
952
+ <CurrentComponent />
953
+ </View>
954
+ </View>
955
+ );
956
+ };
957
+
958
+ const styles = StyleSheet.create({
959
+ container: {
960
+ flex: 1,
961
+ backgroundColor: "#fff",
962
+ },
963
+ tabBar: {
964
+ height: 50,
965
+ backgroundColor: "#f5f5f5",
966
+ borderBottomWidth: 1,
967
+ borderBottomColor: "#e0e0e0",
968
+ },
969
+ tab: {
970
+ paddingHorizontal: 16,
971
+ height: 50,
972
+ justifyContent: "center",
973
+ alignItems: "center",
974
+ },
975
+ activeTab: {
976
+ borderBottomWidth: 2,
977
+ borderBottomColor: "#1652f0",
978
+ },
979
+ tabText: {
980
+ fontSize: 14,
981
+ color: "#666",
982
+ },
983
+ activeTabText: {
984
+ color: "#1652f0",
985
+ fontWeight: "600",
986
+ },
987
+ content: {
988
+ flex: 1,
989
+ },
990
+ emptyText: {
991
+ fontSize: 18,
992
+ color: "#999",
993
+ },
994
+ });
995
+
996
+ export default App;
997
+ INDEX_EOF
998
+
999
+ ok " 已更新 src/index.tsx → 多 demo 切换器 (${#ENTRY_FILES[@]} 个 demo)"
1000
+ fi
1001
+ fi
1002
+
1003
+ echo ""
1004
+
1005
+ # ════════════════════════════════════════════════════════════
1006
+ # 阶段 6: 汇总输出
1007
+ # ════════════════════════════════════════════════════════════
1008
+
1009
+ echo "=========================================="
1010
+ echo " 移植完成"
1011
+ echo "=========================================="
1012
+ echo ""
1013
+
1014
+ info "汇总:"
1015
+ echo " 仓库: ${GIT_URL} (${GIT_BRANCH})"
1016
+ echo " 克隆到: ${CLONE_DIR}"
1017
+ echo ""
1018
+ echo " demo/用例目录 (${#DETECTED_DEMO_DIRS[@]} 个):"
1019
+ for dir in "${DETECTED_DEMO_DIRS[@]}"; do
1020
+ echo " ✓ ${dir}/"
1021
+ done
1022
+ echo ""
1023
+ if [[ ${#ENTRY_FILES[@]} -gt 0 ]]; then
1024
+ echo " 入口文件 (${#ENTRY_FILES[@]} 个):"
1025
+ for entry in "${ENTRY_FILES[@]}"; do
1026
+ entry_dir_name="${entry%%|*}"
1027
+ entry_file_name="${entry##*|}"
1028
+ if [[ ${#ENTRY_FILES[@]} -eq 1 ]]; then
1029
+ echo " ✓ ${entry_dir_name}/${entry_file_name} → src/index.tsx (内容已写入)"
1030
+ else
1031
+ echo " ✓ ${entry_dir_name}/${entry_file_name} → src/${entry_dir_name}/${entry_file_name}"
1032
+ fi
1033
+ done
1034
+ echo ""
1035
+ fi
1036
+ echo " 支撑文件 (assets/animations/模块等) 已拷贝到: src/"
1037
+ echo " 模板工程: ${TEMPLATE_DIR}"
1038
+ echo ""
1039
+
1040
+ ok "测试 Demo/用例移植完成!"
1041
+ echo ""
1042
+ info "后续步骤:"
1043
+ echo " 1. 检查 src/index.tsx 中的导入路径是否正确"
1044
+ echo " 2. 检查 demo 中是否有平台特定代码需要适配 (如 Platform.OS === 'ios')"
1045
+ echo " 3. cd ${TEMPLATE_DIR}"
1046
+ echo " 4. npm run codegen"
1047
+ echo " 5. npm run dev"
1048
+ echo " 6. 用 DevEco Studio 打开 harmony/ 目录构建 HAP"
1049
+ echo ""
1050
+ warn "注意:"
1051
+ echo " - demo 中的动画/图片等资源文件已拷贝到 src/ 下, 请确认路径引用正确"
1052
+ echo " - 如果 demo 依赖了平台原生模块 (如 CameraPicker), 需要额外集成鸿蒙化版本"
1053
+ echo " - 原始 index.tsx 已备份为 index.backup.tsx"