@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.
- package/README.md +290 -244
- package/bin/3rdloop.mjs +46 -46
- package/lib/cli.js +781 -781
- package/lib/config-cmd.js +451 -451
- package/lib/config.js +318 -318
- package/lib/doctor.js +113 -113
- package/lib/exit-codes.js +30 -30
- package/lib/opencode.js +155 -155
- package/lib/orch.js +426 -426
- package/lib/queries.js +263 -263
- package/lib/step.js +325 -325
- package/lib/ui.js +222 -222
- package/lib/update.js +279 -279
- package/lib/workflow.js +308 -308
- package/lib/workflows.js +131 -131
- package/package.json +50 -50
- package/vendor/Server/Agent/SkillSelector/README.md +343 -343
- package/vendor/Server/Agent/SkillSelector/core/Agent.js +207 -207
- package/vendor/Server/Agent/SkillSelector/index.js +14 -14
- package/vendor/Server/Agent/SkillSelector/llm/llmClient.js +313 -313
- package/vendor/Server/Agent/SkillSelector/llm/prompts.js +161 -161
- package/vendor/Server/Agent/SkillSelector/llm/reactSteps.js +144 -144
- package/vendor/Server/Agent/SkillSelector/matcher/fuzzyMatcher.js +433 -433
- package/vendor/Server/Agent/SkillSelector/scanner/skillScanner.js +382 -382
- package/vendor/Server/Agent/SkillSelector/utils/actionWords.js +59 -59
- package/vendor/Server/Brain/AnalysisStrategies.js +422 -422
- package/vendor/Server/Brain/Brain.js +686 -686
- package/vendor/Server/Brain/DecompositionStrategies.js +621 -621
- package/vendor/Server/Brain/PlanPersistence.js +196 -196
- package/vendor/Server/Brain/SkillExecutor.js +251 -251
- package/vendor/Server/Brain/index.js +11 -11
- package/vendor/Server/CLI/cli.js +275 -275
- package/vendor/Server/CLI/deveco-code/index.js +71 -71
- package/vendor/Server/CLI/deveco-code.js +5 -5
- package/vendor/Server/CLI/opencode/index.js +817 -818
- package/vendor/Server/CLI/opencode.js +5 -5
- package/vendor/Server/DbUse/MessageArchive.js +61 -61
- package/vendor/Server/DbUse/StorageManager.js +107 -107
- package/vendor/Server/DbUse/adapters/BaseAdapter.js +94 -94
- package/vendor/Server/DbUse/adapters/FileAdapter.js +138 -138
- package/vendor/Server/DbUse/adapters/JsonAdapter.js +138 -138
- package/vendor/Server/DbUse/adapters/JsonlAdapter.js +114 -114
- package/vendor/Server/DbUse/adapters/SqliteAdapter.js +157 -157
- package/vendor/Server/DbUse/index.js +160 -160
- package/vendor/Server/DbUse/stores/SessionStore.js +279 -279
- package/vendor/Server/DbUse/stores/TaskExecutionStore.js +133 -133
- package/vendor/Server/DbUse/stores/TaskRegistryStore.js +262 -262
- package/vendor/Server/DbUse/stores/TaskStepStore.js +279 -279
- package/vendor/Server/FlexRunner/FlexRunner.js +1759 -1759
- package/vendor/Server/FlexRunner/index.js +12 -12
- package/vendor/Server/Knowledge/KnowledgeImporter.js +523 -523
- package/vendor/Server/Knowledge/KnowledgeManager.js +341 -341
- package/vendor/Server/Knowledge/index.js +33 -33
- package/vendor/Server/LoopEngine/LoopEngine.js +1133 -1133
- package/vendor/Server/LoopEngine/index.js +11 -11
- package/vendor/Server/Orchestrator/Orchestrator.js +0 -3
- package/vendor/Server/Orchestrator/StepNode.js +312 -312
- package/vendor/Server/Orchestrator/index.js +13 -13
- package/vendor/Server/Routes/controllers/BrainController.js +241 -241
- package/vendor/Server/Routes/controllers/FlexRunnerController.js +1125 -1125
- package/vendor/Server/Routes/controllers/HealthController.js +44 -44
- package/vendor/Server/Routes/controllers/KnowledgeImportController.js +355 -355
- package/vendor/Server/Routes/controllers/LoopEngineController.js +788 -788
- package/vendor/Server/Routes/controllers/StatsController.js +57 -57
- package/vendor/Server/Routes/controllers/StreamController.js +67 -67
- package/vendor/Server/Routes/library/logger.js +179 -179
- package/vendor/Server/Routes/library/zipArchive.js +172 -172
- package/vendor/Server/Routes/router/Router.js +204 -204
- package/vendor/Server/Routes/routes/brain.js +47 -47
- package/vendor/Server/Routes/routes/flexrunner.js +72 -72
- package/vendor/Server/Routes/routes/health.js +28 -28
- package/vendor/Server/Routes/routes/index.js +57 -57
- package/vendor/Server/Routes/routes/knowledge.js +55 -55
- package/vendor/Server/Routes/routes/loopengine.js +58 -58
- package/vendor/Server/Routes/routes/orchestrator.js +61 -61
- package/vendor/Server/Routes/routes/registry.js +97 -97
- package/vendor/Server/Routes/routes/stats.js +23 -23
- package/vendor/Server/Routes/routes/stream.js +30 -30
- package/vendor/Server/Routes/server.js +203 -203
- package/vendor/Server/Routes/sse/SseHub.js +308 -308
- package/vendor/Server/Skills/acceptance-check/SKILL.md +191 -191
- package/vendor/Server/Skills/acceptance-check/references/PHASE1_ARTIFACT_COLLECTION.md +52 -52
- package/vendor/Server/Skills/acceptance-check/references/PHASE2_CRITERIA_EVALUATION.md +77 -77
- package/vendor/Server/Skills/acceptance-check/references/PHASE2_EVIDENCE_VERIFICATION.md +70 -70
- package/vendor/Server/Skills/arkts-code-check/SKILL.md +320 -320
- package/vendor/Server/Skills/arkts-code-check/references/CODE_CHECK_GUIDE.md +210 -210
- package/vendor/Server/Skills/arkts-code-check/references/FILE_TYPE_GUIDE.md +28 -28
- package/vendor/Server/Skills/arkts-code-check/references/PHASE1_INFO_COLLECTION.md +233 -233
- package/vendor/Server/Skills/arkts-code-check/references/REPORT_TEMPLATE.md +219 -219
- package/vendor/Server/Skills/arkts-library-compile-validate/SKILL.md +328 -328
- package/vendor/Server/Skills/arkts-library-compile-validate/scripts/build.ps1 +93 -93
- package/vendor/Server/Skills/arkts-library-compile-validate/scripts/code-linter.cjs +432 -432
- package/vendor/Server/Skills/arkts-library-compile-validate/scripts/migrate.cjs +851 -851
- package/vendor/Server/Skills/arkts-library-compile-validate/scripts/ohpm-switch.cjs +788 -788
- package/vendor/Server/Skills/arkts-library-demo-code/SKILL.md +291 -291
- package/vendor/Server/Skills/arkts-library-demo-code/references/API_SIGNATURE_CHECKS.md +68 -68
- package/vendor/Server/Skills/arkts-library-demo-code/references/BUILD_VALIDATION.md +184 -184
- package/vendor/Server/Skills/arkts-library-demo-code/references/CODE_STANDARDS.md +337 -337
- package/vendor/Server/Skills/arkts-library-demo-code/references/EXAMPLES.md +400 -400
- package/vendor/Server/Skills/arkts-library-demo-code/references/SYSTEM_API_LOOKUP.md +98 -98
- package/vendor/Server/Skills/arkts-library-demo-code/references/TEMPLATE.md +544 -544
- package/vendor/Server/Skills/arkts-library-demo-code/scripts/build.ps1 +93 -93
- package/vendor/Server/Skills/arkts-library-demo-code/scripts/code-linter.cjs +432 -432
- package/vendor/Server/Skills/arkts-library-demo-code/scripts/migrate.cjs +851 -851
- package/vendor/Server/Skills/arkts-library-demo-code/scripts/ohpm-switch.cjs +788 -788
- package/vendor/Server/Skills/arkts-library-demo-coverage/SKILL.md +174 -174
- package/vendor/Server/Skills/arkts-library-demo-scenario/SKILL.md +321 -321
- package/vendor/Server/Skills/arkts-library-demo-scenario/references/EXAMPLES.md +100 -100
- package/vendor/Server/Skills/arkts-library-demo-scenario/references/TEMPLATE.md +185 -185
- package/vendor/Server/Skills/arkts-library-documentation-quality-check/SKILL.md +233 -233
- package/vendor/Server/Skills/arkts-library-documentation-quality-check/assets/REPORT_TEMPLATE.md +102 -102
- package/vendor/Server/Skills/arkts-library-documentation-quality-check/references/CHECK_DIMENSIONS.md +89 -89
- package/vendor/Server/Skills/arkts-library-documentation-quality-check/references/COMPLIANCE_CHECKLIST.md +62 -62
- package/vendor/Server/Skills/arkts-library-documentation-quality-check/references/KB_QUERY_GUIDE.md +107 -107
- package/vendor/Server/Skills/arkts-library-documentation-quality-check/references/MCP_TOOL_DISCOVERY.md +124 -124
- package/vendor/Server/Skills/arkts-library-documentation-quality-check/references/REPORT_TEMPLATE.md +90 -90
- package/vendor/Server/Skills/arkts-library-documentation-quality-check/references/SCORING_FORMULA.md +57 -57
- package/vendor/Server/Skills/arkts-library-documentation-quality-check/scripts/validate-skill.cjs +259 -259
- package/vendor/Server/Skills/arkts-library-interface-spec/SKILL.md +97 -97
- package/vendor/Server/Skills/arkts-library-memory-leak-check/SKILL.md +391 -391
- package/vendor/Server/Skills/arkts-library-source-clone/SKILL.md +113 -113
- package/vendor/Server/Skills/arkts-library-stability-check/SKILL.md +312 -312
- package/vendor/Server/Skills/arkts-library-stability-check/assets/REPORT_TEMPLATE.md +154 -154
- package/vendor/Server/Skills/arkts-library-stability-check/references/CHECKLIST.md +165 -165
- package/vendor/Server/Skills/arkts-library-stability-check/references/CODE_EXAMPLES.md +308 -308
- package/vendor/Server/Skills/arkts-library-stability-check/references/SCORING_RULES.md +90 -90
- package/vendor/Server/Skills/arkts-library-stability-check/scripts/validate-skill.cjs +282 -282
- package/vendor/Server/Skills/arkts-library-test-coverage-check/SKILL.md +360 -360
- package/vendor/Server/Skills/arkts-library-test-coverage-check/assets/REPORT_TEMPLATE.md +145 -145
- package/vendor/Server/Skills/arkts-library-test-coverage-check/references/CHECKLIST.md +252 -252
- package/vendor/Server/Skills/arkts-library-test-coverage-check/references/MCP_TOOL_USAGE.md +114 -114
- package/vendor/Server/Skills/arkts-library-test-coverage-check/scripts/validate-skill.cjs +262 -262
- package/vendor/Server/Skills/arkts-library-xts-code/SKILL.md +455 -455
- package/vendor/Server/Skills/arkts-library-xts-code/assets/report-template.md +60 -60
- package/vendor/Server/Skills/arkts-library-xts-code/references/ACCESS_MODIFIER_PRECHECK.md +76 -76
- package/vendor/Server/Skills/arkts-library-xts-code/references/BUILD_VERIFICATION.md +200 -200
- package/vendor/Server/Skills/arkts-library-xts-code/references/CODE_STANDARDS.md +518 -518
- package/vendor/Server/Skills/arkts-library-xts-code/references/SUBAGENT_PROMPT_TEMPLATE.md +276 -276
- package/vendor/Server/Skills/arkts-library-xts-code/references/SYSTEM_API_LOOKUP.md +92 -92
- package/vendor/Server/Skills/arkts-library-xts-code/references/XTS_TEMPLATE.md +767 -767
- package/vendor/Server/Skills/arkts-library-xts-code/scripts/build.ps1 +93 -93
- package/vendor/Server/Skills/arkts-library-xts-code/scripts/code-linter.cjs +432 -432
- package/vendor/Server/Skills/arkts-library-xts-code/scripts/migrate.cjs +851 -851
- package/vendor/Server/Skills/arkts-library-xts-code/scripts/ohpm-switch.cjs +788 -788
- package/vendor/Server/Skills/arkts-library-xts-code/scripts/precheck-access-modifiers.cjs +221 -221
- package/vendor/Server/Skills/arkts-library-xts-code-fix/SKILL.md +228 -228
- package/vendor/Server/Skills/arkts-library-xts-coverage/SKILL.md +97 -97
- package/vendor/Server/Skills/arkts-library-xts-doc/SKILL.md +194 -194
- package/vendor/Server/Skills/arkts-library-xts-doc/references/EXAMPLES.md +575 -575
- package/vendor/Server/Skills/arkts-library-xts-doc/references/TEMPLATE.md +574 -574
- package/vendor/Server/Skills/arkts-library-xts-doc/references/TESTABILITY_GUIDE.md +182 -182
- package/vendor/Server/Skills/arkts-library-xts-execution/SKILL.md +490 -490
- package/vendor/Server/Skills/arkts-library-xts-execution/references/error-fixing-examples.md +217 -217
- package/vendor/Server/Skills/arkts-library-xts-execution/scripts/build.ps1 +93 -93
- package/vendor/Server/Skills/arkts-library-xts-execution/scripts/code-linter.cjs +432 -432
- package/vendor/Server/Skills/arkts-library-xts-execution/scripts/migrate.cjs +851 -851
- package/vendor/Server/Skills/arkts-library-xts-execution/scripts/ohpm-switch.cjs +788 -788
- package/vendor/Server/Skills/flutter-code-check/SKILL.md +299 -299
- package/vendor/Server/Skills/flutter-code-check/assets/OUTPUT_TEMPLATE.md +311 -311
- package/vendor/Server/Skills/flutter-code-check/references/CHECKLIST.md +190 -190
- package/vendor/Server/Skills/flutter-code-check/references/PLATFORMS.md +133 -133
- package/vendor/Server/Skills/flutter-code-check/scripts/validate-skill.cjs +259 -259
- package/vendor/Server/Skills/flutter-interface/README.md +75 -75
- package/vendor/Server/Skills/flutter-interface/SKILL.md +291 -291
- package/vendor/Server/Skills/flutter-interface/references/EXAMPLES.md +336 -336
- package/vendor/Server/Skills/flutter-interface/references/GIT_CLONE_GUIDE.md +190 -190
- package/vendor/Server/Skills/flutter-interface/references/SCRIPT_REFERENCE.md +258 -258
- package/vendor/Server/Skills/flutter-interface/references/TEMPLATE.md +254 -254
- package/vendor/Server/Skills/flutter-library-consistency-check/SKILL.md +517 -517
- package/vendor/Server/Skills/flutter-library-consistency-check/references/CHECKLIST.md +381 -381
- package/vendor/Server/Skills/flutter-library-consistency-check/references/FUNCTION_COMPARISON_GUIDE.md +341 -341
- package/vendor/Server/Skills/flutter-library-consistency-check/references/PLATFORM_LIMITATION_GUIDE.md +71 -71
- package/vendor/Server/Skills/flutter-library-documentation-quality-check/SKILL.md +518 -518
- package/vendor/Server/Skills/flutter-library-documentation-quality-check/references/CHECKLIST.md +406 -406
- package/vendor/Server/Skills/flutter-library-memory-leak-check/SKILL.md +652 -652
- package/vendor/Server/Skills/flutter-library-memory-leak-check/references/CHECKLIST.md +396 -396
- package/vendor/Server/Skills/flutter-library-stability-check/SKILL.md +744 -744
- package/vendor/Server/Skills/flutter-library-stability-check/references/CHECKLIST.md +720 -720
- package/vendor/Server/Skills/flutter-library-test-coverage-check/SKILL.md +414 -414
- package/vendor/Server/Skills/flutter-library-test-coverage-check/assets/REPORT_TEMPLATE.md +150 -150
- package/vendor/Server/Skills/flutter-library-test-coverage-check/references/CHECKLIST.md +304 -304
- package/vendor/Server/Skills/flutter-library-test-coverage-check/references/SCORING_RULES.md +265 -265
- package/vendor/Server/Skills/flutter-library-test-coverage-check/references/SCRIPT_USAGE.md +286 -286
- package/vendor/Server/Skills/knowledge-import/SKILL.md +383 -383
- package/vendor/Server/Skills/knowledge-import/references/MCP_TOOL_USAGE.md +165 -165
- package/vendor/Server/Skills/knowledge-import/references/SKILL_LOCATOR_GUIDE.md +91 -91
- package/vendor/Server/Skills/knowledge-import/references/SKILL_OPTIMIZATION_GUIDE.md +127 -127
- package/vendor/Server/Skills/ohos-lib-pr-push/SKILL.md +453 -301
- package/vendor/Server/Skills/ohos-lib-pr-push/assets/PR_CHECKLIST.md +151 -0
- package/vendor/Server/Skills/ohos-lib-pr-push/assets/PR_ISSUE_TEMPLATE.md +257 -105
- package/vendor/Server/Skills/ohos-lib-pr-push/references/GITCODE_API.md +191 -127
- package/vendor/Server/Skills/ohos-lib-pr-push/scripts/gitcode-api.cjs +317 -208
- package/vendor/Server/Skills/rn-code-check/SKILL.md +320 -320
- package/vendor/Server/Skills/rn-code-check/references/API-GUIDE.md +234 -234
- package/vendor/Server/Skills/rn-code-check/references/CHECKLIST.md +188 -188
- package/vendor/Server/Skills/rn-code-check/references/REPORT-TEMPLATE.md +246 -246
- package/vendor/Server/Skills/rn-lib-add-example/SKILL.md +198 -198
- package/vendor/Server/Skills/rn-lib-add-example/references/APP_RENAME_GUIDE.md +40 -40
- package/vendor/Server/Skills/rn-lib-add-example/references/SHELL_ADAPTATION_CHECKLIST.md +78 -78
- package/vendor/Server/Skills/rn-lib-add-example/references/TEMPLATE_REPO_GUIDE.md +70 -70
- package/vendor/Server/Skills/rn-lib-add-example/references/VERIFICATION_GUIDE.md +47 -47
- package/vendor/Server/Skills/rn-lib-add-example/scripts/create-example.cjs +252 -252
- package/vendor/Server/Skills/rn-lib-update-doc/SKILL.md +176 -176
- package/vendor/Server/Skills/rn-lib-update-doc/config.json +5 -5
- package/vendor/Server/Skills/rn-lib-update-doc/references/changelog-template.md +35 -35
- package/vendor/Server/Skills/rn-lib-update-doc/references/format-diff.md +137 -137
- package/vendor/Server/Skills/rn-lib-update-doc/references/link-registry.json +33 -33
- package/vendor/Server/Skills/rn-lib-update-doc/references/model-0.4.2-overview.md +40 -40
- package/vendor/Server/Skills/rn-lib-update-doc/references/model-0.4.2.md +429 -429
- package/vendor/Server/Skills/rn-lib-update-doc/scripts/detect-readme-format.cjs +105 -105
- package/vendor/Server/Skills/rn-lib-update-doc/scripts/find-usage-doc.cjs +90 -90
- package/vendor/Server/Skills/rn-lib-update-doc/scripts/link-verify.cjs +213 -213
- package/vendor/Server/Skills/rn-lib-update-doc/scripts/normalize-changelog.cjs +73 -73
- package/vendor/Server/Skills/rn-lib-update-doc/scripts/normalize-package-meta.cjs +258 -258
- package/vendor/Server/Skills/rn-lib-update-doc/scripts/normalize-readme-links.cjs +341 -341
- package/vendor/Server/Skills/rn-lib-update-doc/scripts/resolve-usage-docs.cjs +67 -67
- package/vendor/Server/Skills/rn-library-consistency-check/SKILL.md +299 -299
- package/vendor/Server/Skills/rn-library-consistency-check/assets/REPORT_TEMPLATE.md +293 -293
- package/vendor/Server/Skills/rn-library-consistency-check/references/ARKTS_COMPONENT_CHECK_GUIDE.md +857 -857
- package/vendor/Server/Skills/rn-library-consistency-check/references/ARKTS_TURBOMODULE_CHECK_GUIDE.md +856 -856
- package/vendor/Server/Skills/rn-library-consistency-check/references/CHECKLIST.md +157 -157
- package/vendor/Server/Skills/rn-library-consistency-check/references/CPP_COMPONENT_CHECK_GUIDE.md +801 -801
- package/vendor/Server/Skills/rn-library-consistency-check/references/CPP_TURBOMODULE_CHECK_GUIDE.md +683 -683
- package/vendor/Server/Skills/rn-library-consistency-check/references/FUNCTION_COMPARISON_GUIDE.md +233 -233
- package/vendor/Server/Skills/rn-library-consistency-check/references/NATIVE_COMPONENT_IDENTIFICATION.md +1373 -1373
- package/vendor/Server/Skills/rn-library-consistency-check/references/SCORING_STANDARD.md +220 -220
- package/vendor/Server/Skills/rn-library-consistency-check/scripts/validate-skill.py +245 -245
- package/vendor/Server/Skills/rn-library-documentation-quality-check/SKILL.md +263 -263
- package/vendor/Server/Skills/rn-library-documentation-quality-check/assets/MODEL.md +414 -414
- package/vendor/Server/Skills/rn-library-documentation-quality-check/assets/MODEL_en.md +413 -413
- package/vendor/Server/Skills/rn-library-documentation-quality-check/assets/REPORT_TEMPLATE.md +311 -311
- package/vendor/Server/Skills/rn-library-documentation-quality-check/references/CHECK_CHECKLIST.md +355 -355
- package/vendor/Server/Skills/rn-library-documentation-quality-check/references/CROSS_VALIDATION_GUIDE.md +115 -115
- package/vendor/Server/Skills/rn-library-documentation-quality-check/references/DOC_COLLECTION_GUIDE.md +139 -139
- package/vendor/Server/Skills/rn-library-documentation-quality-check/references/EXAMPLES.md +586 -586
- package/vendor/Server/Skills/rn-library-documentation-quality-check/references/GOTCHAS.md +57 -57
- package/vendor/Server/Skills/rn-library-documentation-quality-check/scripts/validate-skill.py +259 -259
- package/vendor/Server/Skills/rn-library-interface-analyzer/SKILL.md +301 -301
- package/vendor/Server/Skills/rn-library-interface-analyzer/assets/REPORT_TEMPLATE.md +148 -148
- package/vendor/Server/Skills/rn-library-interface-analyzer/references/DOC_ANNOTATION_GUIDE.md +118 -118
- package/vendor/Server/Skills/rn-library-interface-analyzer/references/SCRIPT_USAGE_GUIDE.md +168 -168
- package/vendor/Server/Skills/rn-library-interface-analyzer/references/URL_PARSING_RULES.md +176 -176
- package/vendor/Server/Skills/rn-library-issue-generalizer/SKILL.md +274 -274
- package/vendor/Server/Skills/rn-library-issue-generalizer/assets/case-template.md +45 -45
- package/vendor/Server/Skills/rn-library-issue-generalizer/assets/rule-template.md +25 -25
- package/vendor/Server/Skills/rn-library-issue-generalizer/references/PR_FETCH_GUIDE.md +61 -61
- package/vendor/Server/Skills/rn-library-issue-generalizer/references/RULE_TEMPLATE_GUIDE.md +165 -165
- package/vendor/Server/Skills/rn-library-issue-generalizer/scripts/fetch-pr-info.py +239 -239
- package/vendor/Server/Skills/rn-library-known-issue-check/SKILL.md +263 -263
- package/vendor/Server/Skills/rn-library-known-issue-check/assets/REPORT_TEMPLATE.md +96 -96
- package/vendor/Server/Skills/rn-library-known-issue-check/references/COMPLIANCE_CHECKLIST.md +57 -57
- package/vendor/Server/Skills/rn-library-known-issue-check/references/INSPECTION_GUIDE.md +99 -99
- package/vendor/Server/Skills/rn-library-known-issue-check/references/PRESCREEN_GUIDE.md +64 -64
- package/vendor/Server/Skills/rn-library-known-issue-check/references/SOURCE_PREP_GUIDE.md +74 -74
- package/vendor/Server/Skills/rn-library-known-issue-check/scripts/validate-known-issue-report.py +164 -164
- package/vendor/Server/Skills/rn-library-memory-leak-check/SKILL.md +356 -339
- package/vendor/Server/Skills/rn-library-memory-leak-check/assets/REPORT_TEMPLATE.md +85 -83
- package/vendor/Server/Skills/rn-library-memory-leak-check/references/CHECKLIST.md +905 -859
- package/vendor/Server/Skills/rn-library-memory-leak-check/references/HARMONY_CODE_PATTERNS.md +124 -122
- package/vendor/Server/Skills/rn-library-memory-leak-check/scripts/scan_harmony_code.py +287 -279
- package/vendor/Server/Skills/rn-library-stability-check/SKILL.md +273 -273
- package/vendor/Server/Skills/rn-library-stability-check/assets/REPORT_TEMPLATE.md +129 -129
- package/vendor/Server/Skills/rn-library-stability-check/references/CHECKLIST.md +581 -581
- package/vendor/Server/Skills/rn-library-stability-check/references/SCORING_RULES.md +138 -138
- package/vendor/Server/Skills/rn-library-stability-check/scripts/scan-harmony-code.py +168 -168
- package/vendor/Server/Skills/rn-library-test-coverage-check/SKILL.md +526 -506
- package/vendor/Server/Skills/rn-library-test-coverage-check/references/CHECKLIST.md +321 -280
- package/vendor/Server/Skills/rn-library-test-coverage-check/references/DEMO_LOCATION.md +221 -221
- package/vendor/Server/Skills/rn-library-test-coverage-check/references/REPORT_TEMPLATE.md +220 -209
- package/vendor/Server/Skills/rn-library-test-coverage-check/references/SCRIPT_USAGE.md +325 -325
- package/vendor/Server/Skills/rn-ohos-xts-generator/SKILL.md +279 -279
- package/vendor/Server/Skills/rn-ohos-xts-generator/assets/TEST_FILE_TEMPLATES.md +404 -404
- package/vendor/Server/Skills/rn-ohos-xts-generator/references/BUILD_TROUBLESHOOTING.md +185 -185
- package/vendor/Server/Skills/rn-ohos-xts-generator/references/BUILD_WORKFLOW.md +265 -265
- package/vendor/Server/Skills/rn-ohos-xts-generator/references/COMMON_CONFIG.md +189 -189
- package/vendor/Server/Skills/rn-ohos-xts-generator/references/CONFIG_GUIDE.md +203 -203
- package/vendor/Server/Skills/rn-ohos-xts-generator/references/COVERAGE_ANALYZER.md +147 -147
- package/vendor/Server/Skills/rn-ohos-xts-generator/references/DESIGN_DOC_GUIDE.md +148 -148
- package/vendor/Server/Skills/rn-ohos-xts-generator/references/GENERATION_FLOW.md +165 -165
- package/vendor/Server/Skills/rn-ohos-xts-generator/references/GENERATION_STRATEGY.md +195 -195
- package/vendor/Server/Skills/rn-ohos-xts-generator/references/HARMONY_SCANNER.md +249 -249
- package/vendor/Server/Skills/rn-ohos-xts-generator/references/JEST_FRAMEWORK.md +235 -235
- package/vendor/Server/Skills/rn-ohos-xts-generator/references/PLATFORM_COMPAT.md +161 -161
- package/vendor/Server/Skills/rn-ohos-xts-generator/references/RN_TEST_PATTERNS.md +413 -413
- package/vendor/Server/Skills/rn-ohos-xts-generator/references/SOURCE_PARSER.md +235 -235
- package/vendor/Server/Skills/rn-ohos-xts-generator/references/TEST_CONVENTIONS.md +245 -245
- package/vendor/Server/Skills/rn-ohos-xts-generator/references/TROUBLESHOOTING.md +282 -282
- package/vendor/Server/Skills/rn-ohos-xts-generator/references/USAGE_GUIDE.md +251 -251
- package/vendor/Server/Skills/rnoh-cptf-api-test/SKILL.md +469 -469
- package/vendor/Server/Skills/rnoh-cptf-api-test/assets/CPTF_TEST_TEMPLATE.md +310 -310
- package/vendor/Server/Skills/rnoh-cptf-api-test/assets/DEMO_PAGE_TEMPLATE.md +303 -303
- package/vendor/Server/Skills/rnoh-cptf-api-test/references/CPTF_TEST_GENERATION_GUIDE.md +421 -421
- package/vendor/Server/Skills/rnoh-cptf-api-test/references/DEMO_GENERATION_GUIDE.md +358 -358
- package/vendor/Server/Skills/rnoh-cptf-api-test/references/QUALITY_CHECKLIST.md +114 -114
- package/vendor/Server/Skills/rnoh-cptf-api-test/references/SPEC_PARSING_GUIDE.md +274 -274
- package/vendor/Server/Skills/rnoh-cptf-api-test/scripts/validate-rnoh-cptf-api-test.sh +185 -185
- package/vendor/Server/Skills/rnoh-cptf-demo-test/SKILL.md +191 -191
- package/vendor/Server/Skills/rnoh-cptf-demo-test/assets/TEST_TEMPLATE.md +216 -216
- package/vendor/Server/Skills/rnoh-cptf-demo-test/references/CPTF_SETUP.md +107 -107
- package/vendor/Server/Skills/rnoh-cptf-demo-test/references/DEMO_ANALYSIS.md +120 -120
- package/vendor/Server/Skills/rnoh-cptf-demo-test/scripts/validate-rnoh-cptf-test.sh +108 -108
- package/vendor/Server/Skills/rnoh-cptf-test-run/SKILL.md +237 -237
- package/vendor/Server/Skills/rnoh-cptf-test-run/references/DEBUG_PLAYBOOK.md +261 -261
- package/vendor/Server/Skills/rnoh-cptf-test-run/scripts/validate-run-env.sh +112 -112
- package/vendor/Server/Skills/rnoh-lib-code-adapt/SKILL.md +497 -497
- package/vendor/Server/Skills/rnoh-lib-code-adapt/references/adapt-checklist.md +270 -270
- package/vendor/Server/Skills/rnoh-lib-code-adapt/references/api-migration-checklist.md +164 -164
- package/vendor/Server/Skills/rnoh-lib-code-adapt/references/common-compile-errors.md +556 -556
- package/vendor/Server/Skills/rnoh-lib-code-adapt/references/deveco-cli-tools.md +459 -459
- package/vendor/Server/Skills/rnoh-lib-code-adapt/references/edge-cases.md +65 -65
- package/vendor/Server/Skills/rnoh-lib-code-adapt/references/har-tgz-build.md +512 -512
- package/vendor/Server/Skills/rnoh-lib-code-adapt/references/version-compat-matrix.md +132 -132
- package/vendor/Server/Skills/rnoh-lib-code-adapt/scripts/auto-integrate.sh +424 -424
- package/vendor/Server/Skills/rnoh-lib-demo-coverage/SKILL.md +137 -137
- package/vendor/Server/Skills/rnoh-lib-demo-create/SKILL.md +306 -306
- package/vendor/Server/Skills/rnoh-lib-demo-create/assets/DEMO_PAGE_TEMPLATE.md +485 -485
- package/vendor/Server/Skills/rnoh-lib-demo-create/assets/INDEX_TEMPLATE.md +172 -172
- package/vendor/Server/Skills/rnoh-lib-demo-create/references/DEMO_CODE_RULES.md +422 -422
- package/vendor/Server/Skills/rnoh-lib-demo-create/references/QUALITY_CHECKLIST.md +95 -95
- package/vendor/Server/Skills/rnoh-lib-demo-create/references/SOURCE_EXPLORATION.md +169 -169
- package/vendor/Server/Skills/rnoh-lib-demo-doc/SKILL.md +554 -554
- package/vendor/Server/Skills/rnoh-lib-demo-doc/assets/DEMO_DOC_TEMPLATE.md +106 -106
- package/vendor/Server/Skills/rnoh-lib-demo-doc/references/COVERAGE_INCREMENTAL.md +216 -216
- package/vendor/Server/Skills/rnoh-lib-demo-doc/references/DEMO_DESIGN_RULES.md +413 -413
- package/vendor/Server/Skills/rnoh-lib-demo-doc/references/OUTPUT_FORMAT.md +269 -269
- package/vendor/Server/Skills/rnoh-lib-demo-doc/references/QUALITY_CHECKLIST.md +98 -98
- package/vendor/Server/Skills/rnoh-lib-func-test/SKILL.md +337 -337
- package/vendor/Server/Skills/rnoh-lib-rnt-search/SKILL.md +722 -722
- package/vendor/Server/Skills/rnoh-lib-rnt-search/assets/PORT_REPORT_TEMPLATE.md +219 -219
- package/vendor/Server/Skills/rnoh-lib-rnt-search/references/DEMO_PORTING_GUIDE.md +345 -345
- package/vendor/Server/Skills/rnoh-lib-rnt-search/references/PORT_VERIFICATION.md +269 -269
- package/vendor/Server/Skills/rnoh-lib-rnt-search/references/RNT_PROJECT_STRUCTURE.md +173 -173
- package/vendor/Server/Skills/rnoh-lib-rnt-search/scripts/analyze-demo-deps.cjs +267 -267
- package/vendor/Server/Skills/rnoh-lib-rnt-search/scripts/detect-rnoh-version.cjs +174 -174
- package/vendor/Server/Skills/rnoh-lib-rnt-search/scripts/find-rnt-demo.sh +111 -111
- package/vendor/Server/Skills/rnoh-lib-rnt-search/scripts/port-demo.sh +186 -186
- package/vendor/Server/Skills/rnoh-lib-rnt-search/scripts/read-lib-meta.cjs +150 -150
- package/vendor/Server/Skills/rnoh-lib-test-align/SKILL.md +296 -296
- package/vendor/Server/Skills/rnoh-lib-test-align/references/jest-troubleshooting.md +371 -371
- package/vendor/Server/Skills/rnoh-lib-test-align/references/port-test-demo-guide.md +212 -212
- package/vendor/Server/Skills/rnoh-lib-test-align/references/runtime-troubleshooting.md +345 -345
- package/vendor/Server/Skills/rnoh-lib-test-align/references/unit-test-template.md +223 -223
- package/vendor/Server/Skills/rnoh-lib-test-align/scripts/port-test-demo.sh +1053 -1053
- package/vendor/Server/Skills/rnoh-lib-version-diff/SKILL.md +295 -255
- package/vendor/Server/Skills/rnoh-lib-version-diff/references/OHOS_ADAPTATION.md +82 -77
- package/vendor/Server/Skills/rnoh-lib-version-diff/references/OHOS_API_LOOKUP.md +224 -224
- package/vendor/Server/Skills/rnoh-lib-version-diff/references/script-reference.md +175 -174
- package/vendor/Server/Skills/rnoh-lib-version-diff/scripts/detect-upstream-version.js +967 -967
- package/vendor/Server/Skills/rnoh-lib-version-diff/scripts/generate-diff.js +1798 -1798
- package/vendor/Server/Skills/rnoh-lib-version-diff/scripts/package.json +5 -5
- package/vendor/Server/Skills/rnoh-lib-version-diff/templates/report-template.md +180 -137
- package/vendor/Server/Skills/rnoh-lib-xts-coverage/SKILL.md +145 -145
- package/vendor/Server/Skills/skill-generator/SKILL.md +365 -365
- package/vendor/Server/Skills/skill-generator/assets/SKILL_TEMPLATE.md +183 -183
- package/vendor/Server/Skills/skill-generator/references/COMPLIANCE_CHECKLIST.md +82 -82
- package/vendor/Server/Skills/skill-generator/references/KB_QUERY_GUIDE.md +110 -110
- package/vendor/Server/Skills/skill-generator/references/MCP_TOOL_DISCOVERY.md +122 -122
- package/vendor/Server/Skills/skill-generator/references/SKILL_SPEC.md +121 -121
- package/vendor/Server/Skills/skill-generator/scripts/validate-skill.cjs +259 -259
- package/vendor/Server/Skills/task-analysis/SKILL.md +234 -234
- package/vendor/Server/Skills/task-analysis/assets/OUTPUT_EXAMPLES.md +160 -160
- package/vendor/Server/Skills/task-analysis/references/ACCEPTANCE_CRITERIA_GUIDE.md +190 -190
- package/vendor/Server/Skills/task-analysis/references/ANALYSIS_SCHEMA.md +224 -224
- package/vendor/Server/Skills/task-analysis/references/INTEGRATION_CONTEXT.md +162 -162
- package/vendor/Server/Skills/task-analysis/references/LESSONS_LEARNED.md +55 -55
- package/vendor/Server/Skills/task-analysis/references/REPLAN_CONTEXT_GUIDE.md +126 -126
- package/vendor/Server/Skills/task-analysis/scripts/validate-analysis-output.sh +256 -256
- package/vendor/Server/Skills/task-decomposition/SKILL.md +528 -528
- package/vendor/Server/Skills/task-decomposition/assets/OUTPUT_EXAMPLES.md +451 -451
- package/vendor/Server/Skills/task-decomposition/references/ACCEPTANCE_CRITERIA_GUIDE.md +179 -179
- package/vendor/Server/Skills/task-decomposition/references/DEPENDENCY_GRAPH_GUIDE.md +152 -152
- package/vendor/Server/Skills/task-decomposition/references/INTEGRATION_CONTEXT.md +175 -175
- package/vendor/Server/Skills/task-decomposition/references/REPLAN_CONTEXT_GUIDE.md +195 -195
- package/vendor/Server/Skills/task-decomposition/references/TASK_DECOMPOSITION_TEMPLATES.md +226 -226
- package/vendor/Server/Skills/task-decomposition/scripts/list-available-skills.cjs +263 -263
- package/vendor/Server/Skills/task-decomposition/scripts/skill-blacklist.json +29 -29
- package/vendor/Server/Skills/task-decomposition/scripts/validate-decomposition-output.cjs +376 -376
- package/vendor/Server/Skills/task-decomposition/scripts/validate-param-consistency.cjs +259 -259
- package/vendor/Server/Skills/task-decomposition/scripts/validate-specified-skills.cjs +216 -216
- package/vendor/Server/Skills/task-decomposition/scripts/validate-step-graph.cjs +424 -424
- package/vendor/Server/Skills/tpc-cpp-check/SKILL.md +427 -427
- package/vendor/Server/Skills/tpc-cpp-check/references/CHECKLIST.md +214 -214
- package/vendor/Server/Skills/tpc-cpp-check/references/HPKBUILD_GUIDE.md +235 -235
- package/vendor/Server/Skills/tpc-cpp-check/references/REPORT_TEMPLATE.md +250 -250
- package/vendor/Server/TestCheck/TestCheck.js +775 -775
- package/vendor/Server/TestCheck/index.js +11 -11
- package/vendor/Server/library/McpToolGuide.js +280 -280
- package/vendor/Server/library/StatsService.js +126 -126
- package/vendor/Server/library/cstTime.js +16 -16
- package/vendor/VERSION +3 -3
- package/workflows/pr-check.mjs +105 -105
- package/workflows/pr-push.mjs +192 -192
- package/vendor/Server/Agent/SkillSelector/types.js +0 -63
|
@@ -1,789 +1,789 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* ohpm-switch.cjs — 鸿蒙工程「ohpm 线上版本替换」标准化脚本。
|
|
4
|
-
*
|
|
5
|
-
* 背景:ArkTS 三方库源工程通常以 `file:` 或相对路径依赖本地原生模块
|
|
6
|
-
* (FFmpeg / libyuv / NDK / GN / Ninja 预编译等),在预编译产物缺失或构建链
|
|
7
|
-
* 不完整时 devecocli build 会失败(如 libyuv.a 缺失导致 ninja 报错)。
|
|
8
|
-
* 本脚本将 entry 模块对该库的依赖从本地路径替换为 ohpm 线上发行版本,
|
|
9
|
-
* 绕过源库原生构建问题,并对齐三个 SKILL(DemoCode / XTSCode / XTSExecution)
|
|
10
|
-
* 的「ohpm 线上版本替换」流程。
|
|
11
|
-
*
|
|
12
|
-
* 流程:
|
|
13
|
-
* 1. 扫描工程内所有 oh-package.json5,定位 file: / 相对路径依赖
|
|
14
|
-
* 2. 匹配工程级 build-profile.json5 `modules[]` 中注册的同名本地模块
|
|
15
|
-
* 3. 通过 `ohpm info <pkg>` 查询线上版本列表(或 --version 显式指定)
|
|
16
|
-
* 4. 将 oh-package.json5 中该库依赖值改为线上版本号
|
|
17
|
-
* 5. 从 build-profile.json5 的 modules[] 移除本地模块条目
|
|
18
|
-
* 6. 输出改动清单到 <source>/ohpm-switch-report.json,供 SKILL 报告引用
|
|
19
|
-
*
|
|
20
|
-
* 设计原则:
|
|
21
|
-
* - 零第三方依赖;JSON5 采用文本级正则 + 括号配对操作(与 migrate.cjs 同风格),
|
|
22
|
-
* 保留原文件注释与尾逗号
|
|
23
|
-
* - 不修改 @ohos/hypium 等 devDependencies;只处理 file: / 相对路径依赖
|
|
24
|
-
* - Windows PowerShell 与跨平台兼容(ohpm 调用经 shell,支持 exe/.cmd/.bat)
|
|
25
|
-
* - 修改保留不回滚,通过 report 文件如实记录改动,供最终报告引用
|
|
26
|
-
*
|
|
27
|
-
* 用法:
|
|
28
|
-
* node ohpm-switch.cjs --source <工程根> [--pkg <库包名>] [--version <版本号>]
|
|
29
|
-
* [--dry-run] [--run-install] [--help]
|
|
30
|
-
*
|
|
31
|
-
* 示例:
|
|
32
|
-
* node ohpm-switch.cjs --source D:/proj/ohos_ijkplayer --dry-run
|
|
33
|
-
* node ohpm-switch.cjs --source D:/proj/ohos_ijkplayer --pkg @ohos/ijkplayer --version 2.0.9
|
|
34
|
-
* node ohpm-switch.cjs --source D:/proj/ohos_ijkplayer --run-install
|
|
35
|
-
*
|
|
36
|
-
* 退出码:
|
|
37
|
-
* 0 成功(或 dry-run 定位完成)
|
|
38
|
-
* 1 参数/环境错误、ohpm 不可用且未指定版本、写入失败
|
|
39
|
-
* 2 未找到可替换的本地 file 依赖,或依赖无对应 ohpm 线上版本
|
|
40
|
-
*/
|
|
41
|
-
|
|
42
|
-
'use strict';
|
|
43
|
-
|
|
44
|
-
const fs = require('fs');
|
|
45
|
-
const path = require('path');
|
|
46
|
-
const { execFileSync } = require('child_process');
|
|
47
|
-
|
|
48
|
-
// ── 日志(统一 [ohpm-switch] 前缀) ───────────────────────────
|
|
49
|
-
const PREFIX = '[ohpm-switch]';
|
|
50
|
-
|
|
51
|
-
function info(msg) {
|
|
52
|
-
console.log(`${PREFIX} ${msg}`);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function ok(msg) {
|
|
56
|
-
console.log(`${PREFIX} [OK] ${msg}`);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function warn(msg) {
|
|
60
|
-
console.log(`${PREFIX} [WARN] ${msg}`);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function error(msg) {
|
|
64
|
-
console.error(`${PREFIX} [ERROR] ${msg}`);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// ── 工具函数 ────────────────────────────────────────────────
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* 解析 --key value 形式的命令行参数;--flag 形式的布尔参数值为 true。
|
|
71
|
-
* @returns {Record<string, string|true>}
|
|
72
|
-
*/
|
|
73
|
-
function parseArgs() {
|
|
74
|
-
const args = process.argv.slice(2);
|
|
75
|
-
const parsed = {};
|
|
76
|
-
for (let i = 0; i < args.length; i++) {
|
|
77
|
-
if (args[i].startsWith('--')) {
|
|
78
|
-
const key = args[i].substring(2);
|
|
79
|
-
if (i + 1 < args.length && !args[i + 1].startsWith('--')) {
|
|
80
|
-
parsed[key] = args[i + 1];
|
|
81
|
-
i++;
|
|
82
|
-
} else {
|
|
83
|
-
parsed[key] = true;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
return parsed;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* 读取文件文本,文件不存在返回 null。
|
|
92
|
-
* @param {string} filePath
|
|
93
|
-
* @returns {string|null}
|
|
94
|
-
*/
|
|
95
|
-
function readFileSafe(filePath) {
|
|
96
|
-
try {
|
|
97
|
-
return fs.readFileSync(filePath, 'utf-8');
|
|
98
|
-
} catch {
|
|
99
|
-
return null;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* 转义正则中的特殊字符。
|
|
105
|
-
* @param {string} str
|
|
106
|
-
* @returns {string}
|
|
107
|
-
*/
|
|
108
|
-
function escapeRegExp(str) {
|
|
109
|
-
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* 从指定起始索引查找配对的闭合括号位置。
|
|
114
|
-
* @param {string} text
|
|
115
|
-
* @param {number} openIdx 起始括号的索引
|
|
116
|
-
* @param {string} openChar 左括号字符
|
|
117
|
-
* @param {string} closeChar 右括号字符
|
|
118
|
-
* @returns {number} 配对右括号索引;未找到返回 -1
|
|
119
|
-
*/
|
|
120
|
-
function findMatchingBracket(text, openIdx, openChar, closeChar) {
|
|
121
|
-
let depth = 0;
|
|
122
|
-
let inString = false;
|
|
123
|
-
let quoteChar = '';
|
|
124
|
-
for (let i = openIdx; i < text.length; i++) {
|
|
125
|
-
const ch = text[i];
|
|
126
|
-
if (inString) {
|
|
127
|
-
if (ch === '\\') { i++; continue; }
|
|
128
|
-
if (ch === quoteChar) inString = false;
|
|
129
|
-
continue;
|
|
130
|
-
}
|
|
131
|
-
if (ch === '"' || ch === "'") { inString = true; quoteChar = ch; continue; }
|
|
132
|
-
if (ch === openChar) { depth++; continue; }
|
|
133
|
-
if (ch === closeChar) {
|
|
134
|
-
depth--;
|
|
135
|
-
if (depth === 0) return i;
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
return -1;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* 去除 JSON5 字符串值两端的引号。
|
|
143
|
-
* @param {string} str
|
|
144
|
-
* @returns {string}
|
|
145
|
-
*/
|
|
146
|
-
function unquote(str) {
|
|
147
|
-
const s = str.trim();
|
|
148
|
-
if ((s.startsWith('"') && s.endsWith('"')) || (s.startsWith("'") && s.endsWith("'"))) {
|
|
149
|
-
return s.slice(1, -1);
|
|
150
|
-
}
|
|
151
|
-
return s;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* 在对象文本中查找指定 key 的简单字符串/数字值。
|
|
156
|
-
* @param {string} objText 对象片段(不含外层 {})
|
|
157
|
-
* @param {string} key
|
|
158
|
-
* @returns {string|null}
|
|
159
|
-
*/
|
|
160
|
-
function getFieldValueFrom(objText, key) {
|
|
161
|
-
const escaped = escapeRegExp(key);
|
|
162
|
-
const re = new RegExp(
|
|
163
|
-
`["']?${escaped}["']?\\s*:\\s*("([^"\\\\]|\\\\.)*"|'([^'\\\\]|\\\\.)*'|[0-9]+|true|false|null)`
|
|
164
|
-
);
|
|
165
|
-
const m = re.exec(objText);
|
|
166
|
-
if (!m) return null;
|
|
167
|
-
return m[1];
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// ── 工程扫描 ────────────────────────────────────────────────
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
* 从 build-profile.json5 文本中提取 modules[] 数组内的所有模块对象。
|
|
174
|
-
* 支持 JSON5 中键带引号或不带引号两种写法。
|
|
175
|
-
* @param {string} content
|
|
176
|
-
* @returns {Array<{name: string, srcPath: string, rawStart: number, rawEnd: number}>}
|
|
177
|
-
*/
|
|
178
|
-
function extractModules(content) {
|
|
179
|
-
const match = content.match(/(?:^|[,{:])[ \t]*(?:"modules"|'modules'|modules)[ \t]*:[ \t]*\[/m);
|
|
180
|
-
if (!match) return [];
|
|
181
|
-
const arrStart = match.index + match[0].lastIndexOf('['); // 指向 [
|
|
182
|
-
const arrEnd = findMatchingBracket(content, arrStart, '[', ']');
|
|
183
|
-
if (arrEnd === -1) return [];
|
|
184
|
-
|
|
185
|
-
const arrContent = content.slice(arrStart + 1, arrEnd);
|
|
186
|
-
const result = [];
|
|
187
|
-
let i = 0;
|
|
188
|
-
while (i < arrContent.length) {
|
|
189
|
-
if (arrContent[i] === '{') {
|
|
190
|
-
const objEnd = findMatchingBracket(arrContent, i, '{', '}');
|
|
191
|
-
if (objEnd === -1) break;
|
|
192
|
-
const objStr = arrContent.slice(i + 1, objEnd);
|
|
193
|
-
const nameRaw = getFieldValueFrom(objStr, 'name');
|
|
194
|
-
const srcPathRaw = getFieldValueFrom(objStr, 'srcPath');
|
|
195
|
-
result.push({
|
|
196
|
-
name: nameRaw ? unquote(nameRaw) : '',
|
|
197
|
-
srcPath: srcPathRaw ? unquote(srcPathRaw) : '',
|
|
198
|
-
absStart: arrStart + 1 + i,
|
|
199
|
-
absEnd: arrStart + 1 + objEnd,
|
|
200
|
-
});
|
|
201
|
-
i = objEnd + 1;
|
|
202
|
-
} else {
|
|
203
|
-
i++;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
return result;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
/**
|
|
210
|
-
* 提取 JSON5 对象文本中全部 key: string-value 对。
|
|
211
|
-
* @param {string} objText 对象片段(不含外层 {})
|
|
212
|
-
* @returns {Array<{key: string, value: string}>} 去引号后的键值
|
|
213
|
-
*/
|
|
214
|
-
function extractKeyValuePairs(objText) {
|
|
215
|
-
const pairs = [];
|
|
216
|
-
const re = /("([^"\\]|\\.)*"|'([^'\\]|\\.)*')\s*:\s*("([^"\\]|\\.)*"|'([^'\\]|\\.)*')/g;
|
|
217
|
-
let m;
|
|
218
|
-
while ((m = re.exec(objText)) !== null) {
|
|
219
|
-
pairs.push({ key: unquote(m[1]), value: unquote(m[4]) });
|
|
220
|
-
}
|
|
221
|
-
return pairs;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* 提取对象文本中指定顶层字段(如 dependencies)的对象内部内容。
|
|
226
|
-
* 支持字段键带引号或不带引号写法。
|
|
227
|
-
* @param {string} content
|
|
228
|
-
* @param {string} field 字段名(如 dependencies / devDependencies)
|
|
229
|
-
* @returns {string|null} 找到返回内部文本(不含外层 {}),否则 null
|
|
230
|
-
*/
|
|
231
|
-
function getObjectBody(content, field) {
|
|
232
|
-
const re = new RegExp(`(?:"${escapeRegExp(field)}"|'${escapeRegExp(field)}'|${escapeRegExp(field)})\\s*:\\s*\\{`);
|
|
233
|
-
const m = re.exec(content);
|
|
234
|
-
if (!m) return null;
|
|
235
|
-
const bodyStart = m.index + m[0].lastIndexOf('{'); // 指向 {
|
|
236
|
-
const bodyEnd = findMatchingBracket(content, bodyStart, '{', '}');
|
|
237
|
-
if (bodyEnd === -1) return null;
|
|
238
|
-
return content.slice(bodyStart + 1, bodyEnd);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
/**
|
|
242
|
-
* 递归扫描工程目录树内所有 oh-package.json5,定位 file:/相对路径依赖。
|
|
243
|
-
* 跳过 dependencies/oh_modules/node_modules/.git/build 等目录,避免进入依赖产物目录。
|
|
244
|
-
* @param {string} projectRoot
|
|
245
|
-
* @returns {Array<{pkgFile: string, section: string, depKey: string, depValue: string}>}
|
|
246
|
-
*/
|
|
247
|
-
function scanFileDeps(projectRoot) {
|
|
248
|
-
const IGNORE_DIRS = new Set([
|
|
249
|
-
'oh_modules', 'node_modules', '.git', 'build',
|
|
250
|
-
'default', 'intermediates', '.idea', '.hvigor', '.cxx',
|
|
251
|
-
]);
|
|
252
|
-
const pkgFiles = [];
|
|
253
|
-
|
|
254
|
-
(function walk(dir) {
|
|
255
|
-
let entries;
|
|
256
|
-
try {
|
|
257
|
-
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
258
|
-
} catch {
|
|
259
|
-
return;
|
|
260
|
-
}
|
|
261
|
-
for (const entry of entries) {
|
|
262
|
-
const full = path.join(dir, entry.name);
|
|
263
|
-
if (entry.isDirectory()) {
|
|
264
|
-
if (entry.name.startsWith('.') || IGNORE_DIRS.has(entry.name)) continue;
|
|
265
|
-
walk(full);
|
|
266
|
-
} else if (entry.name === 'oh-package.json5') {
|
|
267
|
-
pkgFiles.push(full);
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
})(projectRoot);
|
|
271
|
-
|
|
272
|
-
const results = [];
|
|
273
|
-
for (const pkgFile of pkgFiles) {
|
|
274
|
-
const content = readFileSafe(pkgFile);
|
|
275
|
-
if (!content) continue;
|
|
276
|
-
|
|
277
|
-
for (const section of ['dependencies', 'devDependencies']) {
|
|
278
|
-
const body = getObjectBody(content, section);
|
|
279
|
-
if (body === null) continue;
|
|
280
|
-
for (const { key, value } of extractKeyValuePairs(body)) {
|
|
281
|
-
const v = value.trim();
|
|
282
|
-
// 排除编译产物/NAPI types 伪依赖(如 "libijkplayer_audio_napi.so": "file:./src/main/cpp/types/...")
|
|
283
|
-
// 这类依赖是库打包后的二进制映射,无法通过 ohpm 线上安装,替换会破坏构建
|
|
284
|
-
if (/\.(so|aar|har)$/i.test(key)) continue;
|
|
285
|
-
// 命中 file: 或相对路径(../ ./ ..\ 等)
|
|
286
|
-
if (/^(file:|\.\.?[\\/])/i.test(v)) {
|
|
287
|
-
results.push({
|
|
288
|
-
pkgFile,
|
|
289
|
-
section,
|
|
290
|
-
depKey: key,
|
|
291
|
-
depValue: v,
|
|
292
|
-
});
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
return results;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
/**
|
|
301
|
-
* 匹配 file 依赖与 build-profile.json5 modules[] 中的本地模块。
|
|
302
|
-
* 依赖键 @ohos/ijkplayer 与本地模块 name=ijkplayer / srcPath=./ijkplayer 匹配规则:
|
|
303
|
-
* - 精确匹配
|
|
304
|
-
* - 去掉 scope(@ohos/ 等)后与模块 name 匹配
|
|
305
|
-
* - 依赖键以 "/<模块name>" 结尾
|
|
306
|
-
* - srcPath 文件目录名与依赖键去 scope 后匹配
|
|
307
|
-
* @param {string} depKey
|
|
308
|
-
* @param {Array<{name: string, srcPath: string}>} modules
|
|
309
|
-
* @returns {{name: string, srcPath: string}|null}
|
|
310
|
-
*/
|
|
311
|
-
function matchModuleForDep(depKey, modules) {
|
|
312
|
-
const normalized = depKey.replace(/^@[^/]+\//, '');
|
|
313
|
-
for (const mod of modules) {
|
|
314
|
-
const srcName = mod.srcPath ? mod.srcPath.split(/[\\/]+/).filter(Boolean).pop() : '';
|
|
315
|
-
if (mod.name && mod.name === normalized) return mod;
|
|
316
|
-
if (mod.name && depKey.endsWith('/' + mod.name)) return mod;
|
|
317
|
-
if (mod.name && normalized === srcName && srcName) return mod;
|
|
318
|
-
if (srcName && srcName === normalized) return mod;
|
|
319
|
-
if (mod.name && normalized.includes(mod.name)) return mod;
|
|
320
|
-
}
|
|
321
|
-
return null;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
// ── ohpm 查询 ───────────────────────────────────────────────
|
|
325
|
-
|
|
326
|
-
/**
|
|
327
|
-
* 探测 ohpm 是否可用。Windows 下经 shell 以兼容 ohpm.cmd/ohpm.bat。
|
|
328
|
-
* @returns {string|null} ohpm 版本字符串;不可用返回 null
|
|
329
|
-
*/
|
|
330
|
-
function checkOhpm() {
|
|
331
|
-
try {
|
|
332
|
-
const out = execFileSync('ohpm', ['--version'], {
|
|
333
|
-
encoding: 'utf-8',
|
|
334
|
-
shell: process.platform === 'win32',
|
|
335
|
-
timeout: 30000,
|
|
336
|
-
});
|
|
337
|
-
const lines = out.split(/\r?\n/).filter((l) => l.trim());
|
|
338
|
-
return lines.length > 0 ? lines[lines.length - 1].trim() : 'unknown';
|
|
339
|
-
} catch {
|
|
340
|
-
return null;
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
/**
|
|
345
|
-
* 执行 `ohpm info <pkg>` 查询线上包信息。
|
|
346
|
-
* @param {string} pkg
|
|
347
|
-
* @returns {string|null} 原始输出;失败返回 null
|
|
348
|
-
*/
|
|
349
|
-
function queryOhpmInfo(pkg) {
|
|
350
|
-
try {
|
|
351
|
-
return execFileSync('ohpm', ['info', pkg], {
|
|
352
|
-
encoding: 'utf-8',
|
|
353
|
-
shell: process.platform === 'win32',
|
|
354
|
-
timeout: 60000,
|
|
355
|
-
});
|
|
356
|
-
} catch (e) {
|
|
357
|
-
const msg = String((e && (e.stderr || e.message)) || '').trim().slice(0, 300);
|
|
358
|
-
warn(`ohpm info ${pkg} 失败: ${msg || '未知错误'}`);
|
|
359
|
-
return null;
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
/**
|
|
364
|
-
* 从任意文本中宽松提取所有语义化版本号。
|
|
365
|
-
* @param {string} text
|
|
366
|
-
* @returns {string[]} 去重后的版本号列表
|
|
367
|
-
*/
|
|
368
|
-
function extractVersions(text) {
|
|
369
|
-
const set = new Set();
|
|
370
|
-
const re = /(\d+\.\d+\.\d+)(?:[-+][0-9A-Za-z.-]+)?/g;
|
|
371
|
-
let m;
|
|
372
|
-
while ((m = re.exec(text)) !== null) {
|
|
373
|
-
set.add(m[0]);
|
|
374
|
-
}
|
|
375
|
-
return [...set];
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
/**
|
|
379
|
-
* 解析语义化版本号为结构化对象。
|
|
380
|
-
* @param {string} version
|
|
381
|
-
* @returns {{major: number, minor: number, patch: number, prerelease: string}}
|
|
382
|
-
*/
|
|
383
|
-
function parseSemver(version) {
|
|
384
|
-
const [mainPart, prePart] = version.split('-');
|
|
385
|
-
const parts = mainPart.split('.');
|
|
386
|
-
return {
|
|
387
|
-
major: parseInt(parts[0] || '0', 10),
|
|
388
|
-
minor: parseInt(parts[1] || '0', 10),
|
|
389
|
-
patch: parseInt(parts[2] || '0', 10),
|
|
390
|
-
prerelease: prePart || '',
|
|
391
|
-
};
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
/**
|
|
395
|
-
* 比较两个语义化版本号(含 prerelease 优先级)。
|
|
396
|
-
* @param {string} v1
|
|
397
|
-
* @param {string} v2
|
|
398
|
-
* @returns {number} -1 | 0 | 1
|
|
399
|
-
*/
|
|
400
|
-
function compareSemver(v1, v2) {
|
|
401
|
-
const p1 = parseSemver(v1);
|
|
402
|
-
const p2 = parseSemver(v2);
|
|
403
|
-
if (p1.major !== p2.major) return p1.major < p2.major ? -1 : 1;
|
|
404
|
-
if (p1.minor !== p2.minor) return p1.minor < p2.minor ? -1 : 1;
|
|
405
|
-
if (p1.patch !== p2.patch) return p1.patch < p2.patch ? -1 : 1;
|
|
406
|
-
if (!p1.prerelease && p2.prerelease) return 1;
|
|
407
|
-
if (p1.prerelease && !p2.prerelease) return -1;
|
|
408
|
-
if (!p1.prerelease && !p2.prerelease) return 0;
|
|
409
|
-
const a = p1.prerelease.split('.');
|
|
410
|
-
const b = p2.prerelease.split('.');
|
|
411
|
-
const maxLen = Math.max(a.length, b.length);
|
|
412
|
-
for (let i = 0; i < maxLen; i++) {
|
|
413
|
-
if (!a[i]) return -1;
|
|
414
|
-
if (!b[i]) return 1;
|
|
415
|
-
const na = parseInt(a[i], 10);
|
|
416
|
-
const nb = parseInt(b[i], 10);
|
|
417
|
-
if (!isNaN(na) && !isNaN(nb)) {
|
|
418
|
-
if (na !== nb) return na < nb ? -1 : 1;
|
|
419
|
-
} else if (a[i] !== b[i]) {
|
|
420
|
-
return a[i] < b[i] ? -1 : 1;
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
return 0;
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
/**
|
|
427
|
-
* 将版本号数组按语义化版本降序排序(最新在前)。
|
|
428
|
-
* @param {string[]} versions
|
|
429
|
-
* @returns {string[]}
|
|
430
|
-
*/
|
|
431
|
-
function sortVersionsDesc(versions) {
|
|
432
|
-
return [...versions].sort((a, b) => compareSemver(b, a));
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
/**
|
|
436
|
-
* 从版本列表挑选最新版本(优先稳定版,全部为预发布时取最高预发布版)。
|
|
437
|
-
* @param {string[]} versions
|
|
438
|
-
* @returns {string|null}
|
|
439
|
-
*/
|
|
440
|
-
function pickLatestVersion(versions) {
|
|
441
|
-
if (!versions || versions.length === 0) return null;
|
|
442
|
-
const sorted = sortVersionsDesc(versions);
|
|
443
|
-
const stable = sorted.filter((v) => !v.includes('-'));
|
|
444
|
-
return stable.length > 0 ? stable[0] : sorted[0];
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
/**
|
|
448
|
-
* 从源工程 README 中判断是否出现 ohpm 安装提示的包名(辅助确认包存在)。
|
|
449
|
-
* @param {string} projectRoot
|
|
450
|
-
* @param {string} pkg
|
|
451
|
-
* @returns {boolean}
|
|
452
|
-
*/
|
|
453
|
-
function hasInstallHint(projectRoot, pkg) {
|
|
454
|
-
for (const name of ['README.md', 'README_zh.md', 'README_CN.md']) {
|
|
455
|
-
const content = readFileSafe(path.join(projectRoot, name));
|
|
456
|
-
if (content && content.includes(`ohpm install ${pkg}`)) return true;
|
|
457
|
-
}
|
|
458
|
-
return false;
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
// ── 文件修改(文本级 JSON5 操作) ───────────────────────────
|
|
462
|
-
|
|
463
|
-
/**
|
|
464
|
-
* 将 oh-package.json5 中指定依赖键的值替换为新版本号(保留注释与尾逗号)。
|
|
465
|
-
* @param {string} content 原始文本
|
|
466
|
-
* @param {string} depKey 依赖键
|
|
467
|
-
* @param {string} newValue 新依赖值(版本号)
|
|
468
|
-
* @returns {string|null} 修改后的文本;未找到返回 null
|
|
469
|
-
*/
|
|
470
|
-
function replaceDependencyValue(content, depKey, newValue) {
|
|
471
|
-
const escaped = escapeRegExp(depKey);
|
|
472
|
-
const patterns = [
|
|
473
|
-
// "key": "value" 或 'key': "value"(值可能含转义)
|
|
474
|
-
new RegExp(`("${escaped}"\\s*:\\s*)("[^"]*"|'[^']*')`),
|
|
475
|
-
new RegExp(`('${escaped}'\\s*:\\s*)("[^"]*"|'[^']*')`),
|
|
476
|
-
];
|
|
477
|
-
for (const p of patterns) {
|
|
478
|
-
if (p.test(content)) {
|
|
479
|
-
return content.replace(p, `$1"${newValue}"`);
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
return null;
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
/**
|
|
486
|
-
* 从 build-profile.json5 文本中删除指定本地模块对象(含前导缩进、行尾与逗号)。
|
|
487
|
-
* @param {string} content 原始文本
|
|
488
|
-
* @param {{absStart: number, absEnd: number}} module 模块对象位置
|
|
489
|
-
* @returns {string|null} 修改后的文本;异常返回 null
|
|
490
|
-
*/
|
|
491
|
-
function removeModuleFromContent(content, module) {
|
|
492
|
-
const start = module.absStart;
|
|
493
|
-
const end = module.absEnd; // 指向 }
|
|
494
|
-
|
|
495
|
-
// 定位对象起行行首
|
|
496
|
-
let lineStart = start;
|
|
497
|
-
while (lineStart > 0 && content[lineStart - 1] !== '\n') lineStart--;
|
|
498
|
-
// 定位对象结行行尾(含换行)
|
|
499
|
-
let lineEnd = end + 1;
|
|
500
|
-
while (lineEnd < content.length && content[lineEnd] !== '\n') lineEnd++;
|
|
501
|
-
if (lineEnd < content.length) lineEnd++; // 含换行
|
|
502
|
-
|
|
503
|
-
// 若 } 后紧跟逗号,一并删除(处理同行逗号)
|
|
504
|
-
if (lineEnd < content.length && content[lineEnd] === ',') {
|
|
505
|
-
lineEnd++;
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
const removed = content.slice(lineStart, lineEnd);
|
|
509
|
-
if (!removed.includes('{')) return null;
|
|
510
|
-
|
|
511
|
-
let result = content.slice(0, lineStart) + content.slice(lineEnd);
|
|
512
|
-
|
|
513
|
-
// 若 } 后逗号换行的情况已覆盖;再清理可能遗留的重复空行
|
|
514
|
-
result = result.replace(/\n{3,}/g, '\n\n');
|
|
515
|
-
|
|
516
|
-
// 若 modules 数组因此为空([ 与 ] 之间仅剩空白),收拢为 []
|
|
517
|
-
const emptyArrRe = /(\bmodules\s*:\s*\[)\s+(\s*\])/m;
|
|
518
|
-
result = result.replace(emptyArrRe, '$1$2');
|
|
519
|
-
|
|
520
|
-
return result;
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
// ── 主流程 ──────────────────────────────────────────────────
|
|
524
|
-
|
|
525
|
-
/**
|
|
526
|
-
* 打印用法。
|
|
527
|
-
*/
|
|
528
|
-
function usage() {
|
|
529
|
-
console.log(`
|
|
530
|
-
用法:
|
|
531
|
-
node ohpm-switch.cjs --source <工程根> [--pkg <库包名>] [--version <版本号>]
|
|
532
|
-
[--dry-run] [--run-install] [--help]
|
|
533
|
-
|
|
534
|
-
选项:
|
|
535
|
-
--source <路径> 目标鸿蒙工程根目录(必填)
|
|
536
|
-
--pkg <包名> 指定要替换的依赖包名(如 @ohos/ijkplayer);缺省处理全部 file 依赖
|
|
537
|
-
--version <版本号> 指定线上版本号(如 2.0.9);缺省通过 ohpm info 自动查询最新稳定版
|
|
538
|
-
--dry-run 只定位与查询,不修改任何文件(在 stdout 打印计划改动)
|
|
539
|
-
--run-install 修改完成后自动执行 ohpm install
|
|
540
|
-
--help 显示本帮助
|
|
541
|
-
|
|
542
|
-
退出码: 0=成功 1=环境/参数/写入错误 2=无 file 依赖或无线上版本
|
|
543
|
-
`);
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
/**
|
|
547
|
-
* 主流程。
|
|
548
|
-
*/
|
|
549
|
-
function main() {
|
|
550
|
-
const args = parseArgs();
|
|
551
|
-
|
|
552
|
-
if (args.help === true) {
|
|
553
|
-
usage();
|
|
554
|
-
process.exit(0);
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
if (!args.source || args.source === true) {
|
|
558
|
-
error('缺少 --source 参数。用法: node ohpm-switch.cjs --source <工程根> [--pkg <库包名>]');
|
|
559
|
-
usage();
|
|
560
|
-
process.exit(1);
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
const projectRoot = path.resolve(String(args.source));
|
|
564
|
-
if (!fs.existsSync(projectRoot) || !fs.statSync(projectRoot).isDirectory()) {
|
|
565
|
-
error(`--source 路径不存在或非目录: ${projectRoot}`);
|
|
566
|
-
process.exit(1);
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
const dryRun = args['dry-run'] === true;
|
|
570
|
-
const runInstall = args['run-install'] === true;
|
|
571
|
-
const pkgFilter = args.pkg ? String(args.pkg) : null;
|
|
572
|
-
const explicitVersion = args.version ? String(args.version).trim() : null;
|
|
573
|
-
|
|
574
|
-
info(`开始 ohpm 线上版本替换分析: ${projectRoot}`);
|
|
575
|
-
info(`模式: ${dryRun ? 'DRY-RUN(不写文件)' : '直接修改'}`);
|
|
576
|
-
|
|
577
|
-
// 0. ohpm 探测
|
|
578
|
-
const ohpmAvailable = checkOhpm();
|
|
579
|
-
if (ohpmAvailable) {
|
|
580
|
-
ok(`ohpm 可用,版本: ${ohpmAvailable}`);
|
|
581
|
-
} else if (explicitVersion) {
|
|
582
|
-
warn('ohpm 不可用,但已指定 --version,跳过 ohpm info 自动查询');
|
|
583
|
-
} else {
|
|
584
|
-
error('ohpm 不可用(ohpm --version 执行失败),且未指定 --version,无法查询线上版本。');
|
|
585
|
-
error('请确保 ohpm 在 PATH 中,或使用 --version 显式指定版本号。');
|
|
586
|
-
process.exit(1);
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
// 1. 扫描 file 依赖
|
|
590
|
-
const deps = scanFileDeps(projectRoot);
|
|
591
|
-
if (deps.length === 0) {
|
|
592
|
-
error('未在任意 oh-package.json5 中发现 file: / 相对路径依赖,无需替换。');
|
|
593
|
-
process.exit(2);
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
// 2. 读取 build-profile modules
|
|
597
|
-
const bpPath = path.join(projectRoot, 'build-profile.json5');
|
|
598
|
-
const bpContent = readFileSafe(bpPath);
|
|
599
|
-
const modules = bpContent ? extractModules(bpContent) : [];
|
|
600
|
-
info(`build-profile.json5: ${bpContent ? `已读取,modules[] 含 ${modules.length} 个模块` : '不存在(跳过模块条目移除)'}`);
|
|
601
|
-
|
|
602
|
-
// 3. 过滤 + 匹配 + 查版本
|
|
603
|
-
const candidates = [];
|
|
604
|
-
for (const dep of deps) {
|
|
605
|
-
if (pkgFilter && dep.depKey !== pkgFilter) continue;
|
|
606
|
-
|
|
607
|
-
const matchedModule = bpContent ? matchModuleForDep(dep.depKey, modules) : null;
|
|
608
|
-
|
|
609
|
-
let targetVersion = explicitVersion || null;
|
|
610
|
-
if (!targetVersion && ohpmAvailable) {
|
|
611
|
-
const out = queryOhpmInfo(dep.depKey);
|
|
612
|
-
if (out) {
|
|
613
|
-
const versions = extractVersions(out);
|
|
614
|
-
if (versions.length > 0) {
|
|
615
|
-
targetVersion = pickLatestVersion(versions);
|
|
616
|
-
info(` ${dep.depKey} 线上可用版本(${versions.length} 个),选取: ${targetVersion}`);
|
|
617
|
-
} else {
|
|
618
|
-
warn(` ohpm info ${dep.depKey} 输出中未提取到版本号`);
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
candidates.push({
|
|
624
|
-
pkgFile: dep.pkgFile,
|
|
625
|
-
section: dep.section,
|
|
626
|
-
depKey: dep.depKey,
|
|
627
|
-
depValue: dep.depValue,
|
|
628
|
-
matchedModule,
|
|
629
|
-
targetVersion,
|
|
630
|
-
});
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
if (candidates.length === 0) {
|
|
634
|
-
error(pkgFilter ? `未找到依赖键为 ${pkgFilter} 的 file/相对路径依赖。` : '未找到匹配的 file 依赖。');
|
|
635
|
-
process.exit(2);
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
// 4. 输出定位结果
|
|
639
|
-
info('=== 定位到本地 file 依赖 ===');
|
|
640
|
-
for (const c of candidates) {
|
|
641
|
-
const relPkg = path.relative(projectRoot, c.pkgFile) || path.basename(c.pkgFile);
|
|
642
|
-
info(` ${c.depKey} (${relPkg} ${c.section}) = ${c.depValue}`);
|
|
643
|
-
if (c.matchedModule) {
|
|
644
|
-
info(` -> 匹配本地模块: name=${c.matchedModule.name}, srcPath=${c.matchedModule.srcPath}`);
|
|
645
|
-
} else {
|
|
646
|
-
warn(` -> build-profile.json5 modules[] 中未注册该模块(仅替换依赖值)`);
|
|
647
|
-
}
|
|
648
|
-
if (c.targetVersion) {
|
|
649
|
-
info(` -> 线上版本: ${c.targetVersion}`);
|
|
650
|
-
} else {
|
|
651
|
-
warn(` -> 未获取到线上版本(需 --version 指定或检查 ohpm registry)`);
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
// 5. dry-run 提前结束
|
|
656
|
-
if (dryRun) {
|
|
657
|
-
info('=== DRY-RUN 计划改动(未写入任何文件) ===');
|
|
658
|
-
for (const c of candidates) {
|
|
659
|
-
if (!c.targetVersion) {
|
|
660
|
-
warn(` [跳过] ${c.depKey}: 无目标版本`);
|
|
661
|
-
continue;
|
|
662
|
-
}
|
|
663
|
-
const relPkg = path.relative(projectRoot, c.pkgFile) || path.basename(c.pkgFile);
|
|
664
|
-
info(` ${relPkg}: "${c.depKey}": "${c.depValue}" -> "${c.targetVersion}"`);
|
|
665
|
-
if (c.matchedModule) {
|
|
666
|
-
info(` build-profile.json5: 移除 modules[] 中 ${c.matchedModule.name}`);
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
process.exit(0);
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
// 6. 确定无版本依赖的处理
|
|
673
|
-
const noVersion = candidates.filter((c) => !c.targetVersion);
|
|
674
|
-
if (noVersion.length > 0) {
|
|
675
|
-
error(`以下依赖未获取到线上版本,中止修改(避免写入不完整改动):`);
|
|
676
|
-
for (const c of noVersion) {
|
|
677
|
-
error(` - ${c.depKey}(原值 ${c.depValue})`);
|
|
678
|
-
}
|
|
679
|
-
error('请为这些包通过 --version 指定版本号,或修正 ohpm registry 后重试。');
|
|
680
|
-
process.exit(2);
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
// 7. 执行修改
|
|
684
|
-
const changes = [];
|
|
685
|
-
let failed = false;
|
|
686
|
-
|
|
687
|
-
for (const c of candidates) {
|
|
688
|
-
// 修改 oh-package.json5
|
|
689
|
-
const pkgContent = readFileSafe(c.pkgFile);
|
|
690
|
-
if (pkgContent === null) {
|
|
691
|
-
error(`读取失败: ${c.pkgFile}`);
|
|
692
|
-
failed = true;
|
|
693
|
-
continue;
|
|
694
|
-
}
|
|
695
|
-
const newContent = replaceDependencyValue(pkgContent, c.depKey, c.targetVersion);
|
|
696
|
-
if (newContent === null || newContent === pkgContent) {
|
|
697
|
-
error(`在 ${c.pkgFile} 中未找到依赖键 ${c.depKey},无法替换`);
|
|
698
|
-
failed = true;
|
|
699
|
-
continue;
|
|
700
|
-
}
|
|
701
|
-
fs.writeFileSync(c.pkgFile, newContent, 'utf-8');
|
|
702
|
-
ok(`${path.relative(projectRoot, c.pkgFile)}: "${c.depKey}": "${c.depValue}" -> "${c.targetVersion}"`);
|
|
703
|
-
changes.push({
|
|
704
|
-
file: path.relative(projectRoot, c.pkgFile) || path.basename(c.pkgFile),
|
|
705
|
-
action: 'replace-dependency',
|
|
706
|
-
key: c.depKey,
|
|
707
|
-
oldValue: c.depValue,
|
|
708
|
-
newValue: c.targetVersion,
|
|
709
|
-
});
|
|
710
|
-
|
|
711
|
-
// 移除 build-profile.json5 中的本地模块条目
|
|
712
|
-
if (c.matchedModule && bpContent) {
|
|
713
|
-
for (const mod of modules) {
|
|
714
|
-
if (
|
|
715
|
-
mod.absStart === c.matchedModule.absStart &&
|
|
716
|
-
mod.absEnd === c.matchedModule.absEnd
|
|
717
|
-
) {
|
|
718
|
-
const removed = removeModuleFromContent(bpContent, mod);
|
|
719
|
-
if (removed !== null) {
|
|
720
|
-
fs.writeFileSync(bpPath, removed, 'utf-8');
|
|
721
|
-
ok(`build-profile.json5: 移除本地模块 ${mod.name}(srcPath=${mod.srcPath})`);
|
|
722
|
-
changes.push({
|
|
723
|
-
file: 'build-profile.json5',
|
|
724
|
-
action: 'remove-module',
|
|
725
|
-
module: mod.name,
|
|
726
|
-
});
|
|
727
|
-
} else {
|
|
728
|
-
error(`build-profile.json5 移除模块 ${mod.name} 失败(文本结构异常)`);
|
|
729
|
-
failed = true;
|
|
730
|
-
}
|
|
731
|
-
break;
|
|
732
|
-
}
|
|
733
|
-
}
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
|
-
|
|
737
|
-
if (failed) {
|
|
738
|
-
error('存在修改失败项,请检查上述错误后重试。已成功的修改保留不回滚,请核对后再继续。');
|
|
739
|
-
process.exit(1);
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
// 8. 可选 ohpm install
|
|
743
|
-
let installResult = null;
|
|
744
|
-
if (runInstall) {
|
|
745
|
-
info('执行 ohpm install ...');
|
|
746
|
-
try {
|
|
747
|
-
execFileSync('ohpm', ['install'], {
|
|
748
|
-
cwd: projectRoot,
|
|
749
|
-
encoding: 'utf-8',
|
|
750
|
-
shell: process.platform === 'win32',
|
|
751
|
-
timeout: 300000,
|
|
752
|
-
stdio: 'inherit',
|
|
753
|
-
});
|
|
754
|
-
installResult = { ok: true };
|
|
755
|
-
ok('ohpm install 执行成功');
|
|
756
|
-
} catch (e) {
|
|
757
|
-
installResult = { ok: false, message: String(e.message || '').slice(0, 300) };
|
|
758
|
-
error('ohpm install 执行失败,请手动在工程目录执行 ohpm install 后重新编译。');
|
|
759
|
-
error(` ${installResult.message}`);
|
|
760
|
-
}
|
|
761
|
-
}
|
|
762
|
-
|
|
763
|
-
// 9. 输出改动清单报告
|
|
764
|
-
const report = {
|
|
765
|
-
tool: 'ohpm-switch',
|
|
766
|
-
version: '1.0.0',
|
|
767
|
-
timestamp: new Date().toISOString(),
|
|
768
|
-
sourcePath: projectRoot,
|
|
769
|
-
dryRun: false,
|
|
770
|
-
ohpmAvailable: !!ohpmAvailable,
|
|
771
|
-
ohpmVersion: ohpmAvailable || null,
|
|
772
|
-
changes,
|
|
773
|
-
runInstall,
|
|
774
|
-
installResult,
|
|
775
|
-
};
|
|
776
|
-
const reportPath = path.join(projectRoot, 'ohpm-switch-report.json');
|
|
777
|
-
fs.writeFileSync(reportPath, JSON.stringify(report, null, 2), 'utf-8');
|
|
778
|
-
ok(`改动清单已写入: ${reportPath}`);
|
|
779
|
-
|
|
780
|
-
if (runInstall && installResult && !installResult.ok) {
|
|
781
|
-
// install 失败但文件修改已生效:返回 1 提醒用户,仍保留 report
|
|
782
|
-
process.exit(1);
|
|
783
|
-
}
|
|
784
|
-
|
|
785
|
-
ok('ohpm 线上版本替换完成。请使用 build.ps1 重新编译验证。');
|
|
786
|
-
process.exit(0);
|
|
787
|
-
}
|
|
788
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* ohpm-switch.cjs — 鸿蒙工程「ohpm 线上版本替换」标准化脚本。
|
|
4
|
+
*
|
|
5
|
+
* 背景:ArkTS 三方库源工程通常以 `file:` 或相对路径依赖本地原生模块
|
|
6
|
+
* (FFmpeg / libyuv / NDK / GN / Ninja 预编译等),在预编译产物缺失或构建链
|
|
7
|
+
* 不完整时 devecocli build 会失败(如 libyuv.a 缺失导致 ninja 报错)。
|
|
8
|
+
* 本脚本将 entry 模块对该库的依赖从本地路径替换为 ohpm 线上发行版本,
|
|
9
|
+
* 绕过源库原生构建问题,并对齐三个 SKILL(DemoCode / XTSCode / XTSExecution)
|
|
10
|
+
* 的「ohpm 线上版本替换」流程。
|
|
11
|
+
*
|
|
12
|
+
* 流程:
|
|
13
|
+
* 1. 扫描工程内所有 oh-package.json5,定位 file: / 相对路径依赖
|
|
14
|
+
* 2. 匹配工程级 build-profile.json5 `modules[]` 中注册的同名本地模块
|
|
15
|
+
* 3. 通过 `ohpm info <pkg>` 查询线上版本列表(或 --version 显式指定)
|
|
16
|
+
* 4. 将 oh-package.json5 中该库依赖值改为线上版本号
|
|
17
|
+
* 5. 从 build-profile.json5 的 modules[] 移除本地模块条目
|
|
18
|
+
* 6. 输出改动清单到 <source>/ohpm-switch-report.json,供 SKILL 报告引用
|
|
19
|
+
*
|
|
20
|
+
* 设计原则:
|
|
21
|
+
* - 零第三方依赖;JSON5 采用文本级正则 + 括号配对操作(与 migrate.cjs 同风格),
|
|
22
|
+
* 保留原文件注释与尾逗号
|
|
23
|
+
* - 不修改 @ohos/hypium 等 devDependencies;只处理 file: / 相对路径依赖
|
|
24
|
+
* - Windows PowerShell 与跨平台兼容(ohpm 调用经 shell,支持 exe/.cmd/.bat)
|
|
25
|
+
* - 修改保留不回滚,通过 report 文件如实记录改动,供最终报告引用
|
|
26
|
+
*
|
|
27
|
+
* 用法:
|
|
28
|
+
* node ohpm-switch.cjs --source <工程根> [--pkg <库包名>] [--version <版本号>]
|
|
29
|
+
* [--dry-run] [--run-install] [--help]
|
|
30
|
+
*
|
|
31
|
+
* 示例:
|
|
32
|
+
* node ohpm-switch.cjs --source D:/proj/ohos_ijkplayer --dry-run
|
|
33
|
+
* node ohpm-switch.cjs --source D:/proj/ohos_ijkplayer --pkg @ohos/ijkplayer --version 2.0.9
|
|
34
|
+
* node ohpm-switch.cjs --source D:/proj/ohos_ijkplayer --run-install
|
|
35
|
+
*
|
|
36
|
+
* 退出码:
|
|
37
|
+
* 0 成功(或 dry-run 定位完成)
|
|
38
|
+
* 1 参数/环境错误、ohpm 不可用且未指定版本、写入失败
|
|
39
|
+
* 2 未找到可替换的本地 file 依赖,或依赖无对应 ohpm 线上版本
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
'use strict';
|
|
43
|
+
|
|
44
|
+
const fs = require('fs');
|
|
45
|
+
const path = require('path');
|
|
46
|
+
const { execFileSync } = require('child_process');
|
|
47
|
+
|
|
48
|
+
// ── 日志(统一 [ohpm-switch] 前缀) ───────────────────────────
|
|
49
|
+
const PREFIX = '[ohpm-switch]';
|
|
50
|
+
|
|
51
|
+
function info(msg) {
|
|
52
|
+
console.log(`${PREFIX} ${msg}`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function ok(msg) {
|
|
56
|
+
console.log(`${PREFIX} [OK] ${msg}`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function warn(msg) {
|
|
60
|
+
console.log(`${PREFIX} [WARN] ${msg}`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function error(msg) {
|
|
64
|
+
console.error(`${PREFIX} [ERROR] ${msg}`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ── 工具函数 ────────────────────────────────────────────────
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* 解析 --key value 形式的命令行参数;--flag 形式的布尔参数值为 true。
|
|
71
|
+
* @returns {Record<string, string|true>}
|
|
72
|
+
*/
|
|
73
|
+
function parseArgs() {
|
|
74
|
+
const args = process.argv.slice(2);
|
|
75
|
+
const parsed = {};
|
|
76
|
+
for (let i = 0; i < args.length; i++) {
|
|
77
|
+
if (args[i].startsWith('--')) {
|
|
78
|
+
const key = args[i].substring(2);
|
|
79
|
+
if (i + 1 < args.length && !args[i + 1].startsWith('--')) {
|
|
80
|
+
parsed[key] = args[i + 1];
|
|
81
|
+
i++;
|
|
82
|
+
} else {
|
|
83
|
+
parsed[key] = true;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return parsed;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* 读取文件文本,文件不存在返回 null。
|
|
92
|
+
* @param {string} filePath
|
|
93
|
+
* @returns {string|null}
|
|
94
|
+
*/
|
|
95
|
+
function readFileSafe(filePath) {
|
|
96
|
+
try {
|
|
97
|
+
return fs.readFileSync(filePath, 'utf-8');
|
|
98
|
+
} catch {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* 转义正则中的特殊字符。
|
|
105
|
+
* @param {string} str
|
|
106
|
+
* @returns {string}
|
|
107
|
+
*/
|
|
108
|
+
function escapeRegExp(str) {
|
|
109
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* 从指定起始索引查找配对的闭合括号位置。
|
|
114
|
+
* @param {string} text
|
|
115
|
+
* @param {number} openIdx 起始括号的索引
|
|
116
|
+
* @param {string} openChar 左括号字符
|
|
117
|
+
* @param {string} closeChar 右括号字符
|
|
118
|
+
* @returns {number} 配对右括号索引;未找到返回 -1
|
|
119
|
+
*/
|
|
120
|
+
function findMatchingBracket(text, openIdx, openChar, closeChar) {
|
|
121
|
+
let depth = 0;
|
|
122
|
+
let inString = false;
|
|
123
|
+
let quoteChar = '';
|
|
124
|
+
for (let i = openIdx; i < text.length; i++) {
|
|
125
|
+
const ch = text[i];
|
|
126
|
+
if (inString) {
|
|
127
|
+
if (ch === '\\') { i++; continue; }
|
|
128
|
+
if (ch === quoteChar) inString = false;
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
if (ch === '"' || ch === "'") { inString = true; quoteChar = ch; continue; }
|
|
132
|
+
if (ch === openChar) { depth++; continue; }
|
|
133
|
+
if (ch === closeChar) {
|
|
134
|
+
depth--;
|
|
135
|
+
if (depth === 0) return i;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return -1;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* 去除 JSON5 字符串值两端的引号。
|
|
143
|
+
* @param {string} str
|
|
144
|
+
* @returns {string}
|
|
145
|
+
*/
|
|
146
|
+
function unquote(str) {
|
|
147
|
+
const s = str.trim();
|
|
148
|
+
if ((s.startsWith('"') && s.endsWith('"')) || (s.startsWith("'") && s.endsWith("'"))) {
|
|
149
|
+
return s.slice(1, -1);
|
|
150
|
+
}
|
|
151
|
+
return s;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* 在对象文本中查找指定 key 的简单字符串/数字值。
|
|
156
|
+
* @param {string} objText 对象片段(不含外层 {})
|
|
157
|
+
* @param {string} key
|
|
158
|
+
* @returns {string|null}
|
|
159
|
+
*/
|
|
160
|
+
function getFieldValueFrom(objText, key) {
|
|
161
|
+
const escaped = escapeRegExp(key);
|
|
162
|
+
const re = new RegExp(
|
|
163
|
+
`["']?${escaped}["']?\\s*:\\s*("([^"\\\\]|\\\\.)*"|'([^'\\\\]|\\\\.)*'|[0-9]+|true|false|null)`
|
|
164
|
+
);
|
|
165
|
+
const m = re.exec(objText);
|
|
166
|
+
if (!m) return null;
|
|
167
|
+
return m[1];
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// ── 工程扫描 ────────────────────────────────────────────────
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* 从 build-profile.json5 文本中提取 modules[] 数组内的所有模块对象。
|
|
174
|
+
* 支持 JSON5 中键带引号或不带引号两种写法。
|
|
175
|
+
* @param {string} content
|
|
176
|
+
* @returns {Array<{name: string, srcPath: string, rawStart: number, rawEnd: number}>}
|
|
177
|
+
*/
|
|
178
|
+
function extractModules(content) {
|
|
179
|
+
const match = content.match(/(?:^|[,{:])[ \t]*(?:"modules"|'modules'|modules)[ \t]*:[ \t]*\[/m);
|
|
180
|
+
if (!match) return [];
|
|
181
|
+
const arrStart = match.index + match[0].lastIndexOf('['); // 指向 [
|
|
182
|
+
const arrEnd = findMatchingBracket(content, arrStart, '[', ']');
|
|
183
|
+
if (arrEnd === -1) return [];
|
|
184
|
+
|
|
185
|
+
const arrContent = content.slice(arrStart + 1, arrEnd);
|
|
186
|
+
const result = [];
|
|
187
|
+
let i = 0;
|
|
188
|
+
while (i < arrContent.length) {
|
|
189
|
+
if (arrContent[i] === '{') {
|
|
190
|
+
const objEnd = findMatchingBracket(arrContent, i, '{', '}');
|
|
191
|
+
if (objEnd === -1) break;
|
|
192
|
+
const objStr = arrContent.slice(i + 1, objEnd);
|
|
193
|
+
const nameRaw = getFieldValueFrom(objStr, 'name');
|
|
194
|
+
const srcPathRaw = getFieldValueFrom(objStr, 'srcPath');
|
|
195
|
+
result.push({
|
|
196
|
+
name: nameRaw ? unquote(nameRaw) : '',
|
|
197
|
+
srcPath: srcPathRaw ? unquote(srcPathRaw) : '',
|
|
198
|
+
absStart: arrStart + 1 + i,
|
|
199
|
+
absEnd: arrStart + 1 + objEnd,
|
|
200
|
+
});
|
|
201
|
+
i = objEnd + 1;
|
|
202
|
+
} else {
|
|
203
|
+
i++;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return result;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* 提取 JSON5 对象文本中全部 key: string-value 对。
|
|
211
|
+
* @param {string} objText 对象片段(不含外层 {})
|
|
212
|
+
* @returns {Array<{key: string, value: string}>} 去引号后的键值
|
|
213
|
+
*/
|
|
214
|
+
function extractKeyValuePairs(objText) {
|
|
215
|
+
const pairs = [];
|
|
216
|
+
const re = /("([^"\\]|\\.)*"|'([^'\\]|\\.)*')\s*:\s*("([^"\\]|\\.)*"|'([^'\\]|\\.)*')/g;
|
|
217
|
+
let m;
|
|
218
|
+
while ((m = re.exec(objText)) !== null) {
|
|
219
|
+
pairs.push({ key: unquote(m[1]), value: unquote(m[4]) });
|
|
220
|
+
}
|
|
221
|
+
return pairs;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* 提取对象文本中指定顶层字段(如 dependencies)的对象内部内容。
|
|
226
|
+
* 支持字段键带引号或不带引号写法。
|
|
227
|
+
* @param {string} content
|
|
228
|
+
* @param {string} field 字段名(如 dependencies / devDependencies)
|
|
229
|
+
* @returns {string|null} 找到返回内部文本(不含外层 {}),否则 null
|
|
230
|
+
*/
|
|
231
|
+
function getObjectBody(content, field) {
|
|
232
|
+
const re = new RegExp(`(?:"${escapeRegExp(field)}"|'${escapeRegExp(field)}'|${escapeRegExp(field)})\\s*:\\s*\\{`);
|
|
233
|
+
const m = re.exec(content);
|
|
234
|
+
if (!m) return null;
|
|
235
|
+
const bodyStart = m.index + m[0].lastIndexOf('{'); // 指向 {
|
|
236
|
+
const bodyEnd = findMatchingBracket(content, bodyStart, '{', '}');
|
|
237
|
+
if (bodyEnd === -1) return null;
|
|
238
|
+
return content.slice(bodyStart + 1, bodyEnd);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* 递归扫描工程目录树内所有 oh-package.json5,定位 file:/相对路径依赖。
|
|
243
|
+
* 跳过 dependencies/oh_modules/node_modules/.git/build 等目录,避免进入依赖产物目录。
|
|
244
|
+
* @param {string} projectRoot
|
|
245
|
+
* @returns {Array<{pkgFile: string, section: string, depKey: string, depValue: string}>}
|
|
246
|
+
*/
|
|
247
|
+
function scanFileDeps(projectRoot) {
|
|
248
|
+
const IGNORE_DIRS = new Set([
|
|
249
|
+
'oh_modules', 'node_modules', '.git', 'build',
|
|
250
|
+
'default', 'intermediates', '.idea', '.hvigor', '.cxx',
|
|
251
|
+
]);
|
|
252
|
+
const pkgFiles = [];
|
|
253
|
+
|
|
254
|
+
(function walk(dir) {
|
|
255
|
+
let entries;
|
|
256
|
+
try {
|
|
257
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
258
|
+
} catch {
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
for (const entry of entries) {
|
|
262
|
+
const full = path.join(dir, entry.name);
|
|
263
|
+
if (entry.isDirectory()) {
|
|
264
|
+
if (entry.name.startsWith('.') || IGNORE_DIRS.has(entry.name)) continue;
|
|
265
|
+
walk(full);
|
|
266
|
+
} else if (entry.name === 'oh-package.json5') {
|
|
267
|
+
pkgFiles.push(full);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
})(projectRoot);
|
|
271
|
+
|
|
272
|
+
const results = [];
|
|
273
|
+
for (const pkgFile of pkgFiles) {
|
|
274
|
+
const content = readFileSafe(pkgFile);
|
|
275
|
+
if (!content) continue;
|
|
276
|
+
|
|
277
|
+
for (const section of ['dependencies', 'devDependencies']) {
|
|
278
|
+
const body = getObjectBody(content, section);
|
|
279
|
+
if (body === null) continue;
|
|
280
|
+
for (const { key, value } of extractKeyValuePairs(body)) {
|
|
281
|
+
const v = value.trim();
|
|
282
|
+
// 排除编译产物/NAPI types 伪依赖(如 "libijkplayer_audio_napi.so": "file:./src/main/cpp/types/...")
|
|
283
|
+
// 这类依赖是库打包后的二进制映射,无法通过 ohpm 线上安装,替换会破坏构建
|
|
284
|
+
if (/\.(so|aar|har)$/i.test(key)) continue;
|
|
285
|
+
// 命中 file: 或相对路径(../ ./ ..\ 等)
|
|
286
|
+
if (/^(file:|\.\.?[\\/])/i.test(v)) {
|
|
287
|
+
results.push({
|
|
288
|
+
pkgFile,
|
|
289
|
+
section,
|
|
290
|
+
depKey: key,
|
|
291
|
+
depValue: v,
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
return results;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* 匹配 file 依赖与 build-profile.json5 modules[] 中的本地模块。
|
|
302
|
+
* 依赖键 @ohos/ijkplayer 与本地模块 name=ijkplayer / srcPath=./ijkplayer 匹配规则:
|
|
303
|
+
* - 精确匹配
|
|
304
|
+
* - 去掉 scope(@ohos/ 等)后与模块 name 匹配
|
|
305
|
+
* - 依赖键以 "/<模块name>" 结尾
|
|
306
|
+
* - srcPath 文件目录名与依赖键去 scope 后匹配
|
|
307
|
+
* @param {string} depKey
|
|
308
|
+
* @param {Array<{name: string, srcPath: string}>} modules
|
|
309
|
+
* @returns {{name: string, srcPath: string}|null}
|
|
310
|
+
*/
|
|
311
|
+
function matchModuleForDep(depKey, modules) {
|
|
312
|
+
const normalized = depKey.replace(/^@[^/]+\//, '');
|
|
313
|
+
for (const mod of modules) {
|
|
314
|
+
const srcName = mod.srcPath ? mod.srcPath.split(/[\\/]+/).filter(Boolean).pop() : '';
|
|
315
|
+
if (mod.name && mod.name === normalized) return mod;
|
|
316
|
+
if (mod.name && depKey.endsWith('/' + mod.name)) return mod;
|
|
317
|
+
if (mod.name && normalized === srcName && srcName) return mod;
|
|
318
|
+
if (srcName && srcName === normalized) return mod;
|
|
319
|
+
if (mod.name && normalized.includes(mod.name)) return mod;
|
|
320
|
+
}
|
|
321
|
+
return null;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// ── ohpm 查询 ───────────────────────────────────────────────
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* 探测 ohpm 是否可用。Windows 下经 shell 以兼容 ohpm.cmd/ohpm.bat。
|
|
328
|
+
* @returns {string|null} ohpm 版本字符串;不可用返回 null
|
|
329
|
+
*/
|
|
330
|
+
function checkOhpm() {
|
|
331
|
+
try {
|
|
332
|
+
const out = execFileSync('ohpm', ['--version'], {
|
|
333
|
+
encoding: 'utf-8',
|
|
334
|
+
shell: process.platform === 'win32',
|
|
335
|
+
timeout: 30000,
|
|
336
|
+
});
|
|
337
|
+
const lines = out.split(/\r?\n/).filter((l) => l.trim());
|
|
338
|
+
return lines.length > 0 ? lines[lines.length - 1].trim() : 'unknown';
|
|
339
|
+
} catch {
|
|
340
|
+
return null;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* 执行 `ohpm info <pkg>` 查询线上包信息。
|
|
346
|
+
* @param {string} pkg
|
|
347
|
+
* @returns {string|null} 原始输出;失败返回 null
|
|
348
|
+
*/
|
|
349
|
+
function queryOhpmInfo(pkg) {
|
|
350
|
+
try {
|
|
351
|
+
return execFileSync('ohpm', ['info', pkg], {
|
|
352
|
+
encoding: 'utf-8',
|
|
353
|
+
shell: process.platform === 'win32',
|
|
354
|
+
timeout: 60000,
|
|
355
|
+
});
|
|
356
|
+
} catch (e) {
|
|
357
|
+
const msg = String((e && (e.stderr || e.message)) || '').trim().slice(0, 300);
|
|
358
|
+
warn(`ohpm info ${pkg} 失败: ${msg || '未知错误'}`);
|
|
359
|
+
return null;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* 从任意文本中宽松提取所有语义化版本号。
|
|
365
|
+
* @param {string} text
|
|
366
|
+
* @returns {string[]} 去重后的版本号列表
|
|
367
|
+
*/
|
|
368
|
+
function extractVersions(text) {
|
|
369
|
+
const set = new Set();
|
|
370
|
+
const re = /(\d+\.\d+\.\d+)(?:[-+][0-9A-Za-z.-]+)?/g;
|
|
371
|
+
let m;
|
|
372
|
+
while ((m = re.exec(text)) !== null) {
|
|
373
|
+
set.add(m[0]);
|
|
374
|
+
}
|
|
375
|
+
return [...set];
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* 解析语义化版本号为结构化对象。
|
|
380
|
+
* @param {string} version
|
|
381
|
+
* @returns {{major: number, minor: number, patch: number, prerelease: string}}
|
|
382
|
+
*/
|
|
383
|
+
function parseSemver(version) {
|
|
384
|
+
const [mainPart, prePart] = version.split('-');
|
|
385
|
+
const parts = mainPart.split('.');
|
|
386
|
+
return {
|
|
387
|
+
major: parseInt(parts[0] || '0', 10),
|
|
388
|
+
minor: parseInt(parts[1] || '0', 10),
|
|
389
|
+
patch: parseInt(parts[2] || '0', 10),
|
|
390
|
+
prerelease: prePart || '',
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* 比较两个语义化版本号(含 prerelease 优先级)。
|
|
396
|
+
* @param {string} v1
|
|
397
|
+
* @param {string} v2
|
|
398
|
+
* @returns {number} -1 | 0 | 1
|
|
399
|
+
*/
|
|
400
|
+
function compareSemver(v1, v2) {
|
|
401
|
+
const p1 = parseSemver(v1);
|
|
402
|
+
const p2 = parseSemver(v2);
|
|
403
|
+
if (p1.major !== p2.major) return p1.major < p2.major ? -1 : 1;
|
|
404
|
+
if (p1.minor !== p2.minor) return p1.minor < p2.minor ? -1 : 1;
|
|
405
|
+
if (p1.patch !== p2.patch) return p1.patch < p2.patch ? -1 : 1;
|
|
406
|
+
if (!p1.prerelease && p2.prerelease) return 1;
|
|
407
|
+
if (p1.prerelease && !p2.prerelease) return -1;
|
|
408
|
+
if (!p1.prerelease && !p2.prerelease) return 0;
|
|
409
|
+
const a = p1.prerelease.split('.');
|
|
410
|
+
const b = p2.prerelease.split('.');
|
|
411
|
+
const maxLen = Math.max(a.length, b.length);
|
|
412
|
+
for (let i = 0; i < maxLen; i++) {
|
|
413
|
+
if (!a[i]) return -1;
|
|
414
|
+
if (!b[i]) return 1;
|
|
415
|
+
const na = parseInt(a[i], 10);
|
|
416
|
+
const nb = parseInt(b[i], 10);
|
|
417
|
+
if (!isNaN(na) && !isNaN(nb)) {
|
|
418
|
+
if (na !== nb) return na < nb ? -1 : 1;
|
|
419
|
+
} else if (a[i] !== b[i]) {
|
|
420
|
+
return a[i] < b[i] ? -1 : 1;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
return 0;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* 将版本号数组按语义化版本降序排序(最新在前)。
|
|
428
|
+
* @param {string[]} versions
|
|
429
|
+
* @returns {string[]}
|
|
430
|
+
*/
|
|
431
|
+
function sortVersionsDesc(versions) {
|
|
432
|
+
return [...versions].sort((a, b) => compareSemver(b, a));
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* 从版本列表挑选最新版本(优先稳定版,全部为预发布时取最高预发布版)。
|
|
437
|
+
* @param {string[]} versions
|
|
438
|
+
* @returns {string|null}
|
|
439
|
+
*/
|
|
440
|
+
function pickLatestVersion(versions) {
|
|
441
|
+
if (!versions || versions.length === 0) return null;
|
|
442
|
+
const sorted = sortVersionsDesc(versions);
|
|
443
|
+
const stable = sorted.filter((v) => !v.includes('-'));
|
|
444
|
+
return stable.length > 0 ? stable[0] : sorted[0];
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* 从源工程 README 中判断是否出现 ohpm 安装提示的包名(辅助确认包存在)。
|
|
449
|
+
* @param {string} projectRoot
|
|
450
|
+
* @param {string} pkg
|
|
451
|
+
* @returns {boolean}
|
|
452
|
+
*/
|
|
453
|
+
function hasInstallHint(projectRoot, pkg) {
|
|
454
|
+
for (const name of ['README.md', 'README_zh.md', 'README_CN.md']) {
|
|
455
|
+
const content = readFileSafe(path.join(projectRoot, name));
|
|
456
|
+
if (content && content.includes(`ohpm install ${pkg}`)) return true;
|
|
457
|
+
}
|
|
458
|
+
return false;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// ── 文件修改(文本级 JSON5 操作) ───────────────────────────
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* 将 oh-package.json5 中指定依赖键的值替换为新版本号(保留注释与尾逗号)。
|
|
465
|
+
* @param {string} content 原始文本
|
|
466
|
+
* @param {string} depKey 依赖键
|
|
467
|
+
* @param {string} newValue 新依赖值(版本号)
|
|
468
|
+
* @returns {string|null} 修改后的文本;未找到返回 null
|
|
469
|
+
*/
|
|
470
|
+
function replaceDependencyValue(content, depKey, newValue) {
|
|
471
|
+
const escaped = escapeRegExp(depKey);
|
|
472
|
+
const patterns = [
|
|
473
|
+
// "key": "value" 或 'key': "value"(值可能含转义)
|
|
474
|
+
new RegExp(`("${escaped}"\\s*:\\s*)("[^"]*"|'[^']*')`),
|
|
475
|
+
new RegExp(`('${escaped}'\\s*:\\s*)("[^"]*"|'[^']*')`),
|
|
476
|
+
];
|
|
477
|
+
for (const p of patterns) {
|
|
478
|
+
if (p.test(content)) {
|
|
479
|
+
return content.replace(p, `$1"${newValue}"`);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
return null;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* 从 build-profile.json5 文本中删除指定本地模块对象(含前导缩进、行尾与逗号)。
|
|
487
|
+
* @param {string} content 原始文本
|
|
488
|
+
* @param {{absStart: number, absEnd: number}} module 模块对象位置
|
|
489
|
+
* @returns {string|null} 修改后的文本;异常返回 null
|
|
490
|
+
*/
|
|
491
|
+
function removeModuleFromContent(content, module) {
|
|
492
|
+
const start = module.absStart;
|
|
493
|
+
const end = module.absEnd; // 指向 }
|
|
494
|
+
|
|
495
|
+
// 定位对象起行行首
|
|
496
|
+
let lineStart = start;
|
|
497
|
+
while (lineStart > 0 && content[lineStart - 1] !== '\n') lineStart--;
|
|
498
|
+
// 定位对象结行行尾(含换行)
|
|
499
|
+
let lineEnd = end + 1;
|
|
500
|
+
while (lineEnd < content.length && content[lineEnd] !== '\n') lineEnd++;
|
|
501
|
+
if (lineEnd < content.length) lineEnd++; // 含换行
|
|
502
|
+
|
|
503
|
+
// 若 } 后紧跟逗号,一并删除(处理同行逗号)
|
|
504
|
+
if (lineEnd < content.length && content[lineEnd] === ',') {
|
|
505
|
+
lineEnd++;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
const removed = content.slice(lineStart, lineEnd);
|
|
509
|
+
if (!removed.includes('{')) return null;
|
|
510
|
+
|
|
511
|
+
let result = content.slice(0, lineStart) + content.slice(lineEnd);
|
|
512
|
+
|
|
513
|
+
// 若 } 后逗号换行的情况已覆盖;再清理可能遗留的重复空行
|
|
514
|
+
result = result.replace(/\n{3,}/g, '\n\n');
|
|
515
|
+
|
|
516
|
+
// 若 modules 数组因此为空([ 与 ] 之间仅剩空白),收拢为 []
|
|
517
|
+
const emptyArrRe = /(\bmodules\s*:\s*\[)\s+(\s*\])/m;
|
|
518
|
+
result = result.replace(emptyArrRe, '$1$2');
|
|
519
|
+
|
|
520
|
+
return result;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// ── 主流程 ──────────────────────────────────────────────────
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* 打印用法。
|
|
527
|
+
*/
|
|
528
|
+
function usage() {
|
|
529
|
+
console.log(`
|
|
530
|
+
用法:
|
|
531
|
+
node ohpm-switch.cjs --source <工程根> [--pkg <库包名>] [--version <版本号>]
|
|
532
|
+
[--dry-run] [--run-install] [--help]
|
|
533
|
+
|
|
534
|
+
选项:
|
|
535
|
+
--source <路径> 目标鸿蒙工程根目录(必填)
|
|
536
|
+
--pkg <包名> 指定要替换的依赖包名(如 @ohos/ijkplayer);缺省处理全部 file 依赖
|
|
537
|
+
--version <版本号> 指定线上版本号(如 2.0.9);缺省通过 ohpm info 自动查询最新稳定版
|
|
538
|
+
--dry-run 只定位与查询,不修改任何文件(在 stdout 打印计划改动)
|
|
539
|
+
--run-install 修改完成后自动执行 ohpm install
|
|
540
|
+
--help 显示本帮助
|
|
541
|
+
|
|
542
|
+
退出码: 0=成功 1=环境/参数/写入错误 2=无 file 依赖或无线上版本
|
|
543
|
+
`);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* 主流程。
|
|
548
|
+
*/
|
|
549
|
+
function main() {
|
|
550
|
+
const args = parseArgs();
|
|
551
|
+
|
|
552
|
+
if (args.help === true) {
|
|
553
|
+
usage();
|
|
554
|
+
process.exit(0);
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
if (!args.source || args.source === true) {
|
|
558
|
+
error('缺少 --source 参数。用法: node ohpm-switch.cjs --source <工程根> [--pkg <库包名>]');
|
|
559
|
+
usage();
|
|
560
|
+
process.exit(1);
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
const projectRoot = path.resolve(String(args.source));
|
|
564
|
+
if (!fs.existsSync(projectRoot) || !fs.statSync(projectRoot).isDirectory()) {
|
|
565
|
+
error(`--source 路径不存在或非目录: ${projectRoot}`);
|
|
566
|
+
process.exit(1);
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
const dryRun = args['dry-run'] === true;
|
|
570
|
+
const runInstall = args['run-install'] === true;
|
|
571
|
+
const pkgFilter = args.pkg ? String(args.pkg) : null;
|
|
572
|
+
const explicitVersion = args.version ? String(args.version).trim() : null;
|
|
573
|
+
|
|
574
|
+
info(`开始 ohpm 线上版本替换分析: ${projectRoot}`);
|
|
575
|
+
info(`模式: ${dryRun ? 'DRY-RUN(不写文件)' : '直接修改'}`);
|
|
576
|
+
|
|
577
|
+
// 0. ohpm 探测
|
|
578
|
+
const ohpmAvailable = checkOhpm();
|
|
579
|
+
if (ohpmAvailable) {
|
|
580
|
+
ok(`ohpm 可用,版本: ${ohpmAvailable}`);
|
|
581
|
+
} else if (explicitVersion) {
|
|
582
|
+
warn('ohpm 不可用,但已指定 --version,跳过 ohpm info 自动查询');
|
|
583
|
+
} else {
|
|
584
|
+
error('ohpm 不可用(ohpm --version 执行失败),且未指定 --version,无法查询线上版本。');
|
|
585
|
+
error('请确保 ohpm 在 PATH 中,或使用 --version 显式指定版本号。');
|
|
586
|
+
process.exit(1);
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
// 1. 扫描 file 依赖
|
|
590
|
+
const deps = scanFileDeps(projectRoot);
|
|
591
|
+
if (deps.length === 0) {
|
|
592
|
+
error('未在任意 oh-package.json5 中发现 file: / 相对路径依赖,无需替换。');
|
|
593
|
+
process.exit(2);
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
// 2. 读取 build-profile modules
|
|
597
|
+
const bpPath = path.join(projectRoot, 'build-profile.json5');
|
|
598
|
+
const bpContent = readFileSafe(bpPath);
|
|
599
|
+
const modules = bpContent ? extractModules(bpContent) : [];
|
|
600
|
+
info(`build-profile.json5: ${bpContent ? `已读取,modules[] 含 ${modules.length} 个模块` : '不存在(跳过模块条目移除)'}`);
|
|
601
|
+
|
|
602
|
+
// 3. 过滤 + 匹配 + 查版本
|
|
603
|
+
const candidates = [];
|
|
604
|
+
for (const dep of deps) {
|
|
605
|
+
if (pkgFilter && dep.depKey !== pkgFilter) continue;
|
|
606
|
+
|
|
607
|
+
const matchedModule = bpContent ? matchModuleForDep(dep.depKey, modules) : null;
|
|
608
|
+
|
|
609
|
+
let targetVersion = explicitVersion || null;
|
|
610
|
+
if (!targetVersion && ohpmAvailable) {
|
|
611
|
+
const out = queryOhpmInfo(dep.depKey);
|
|
612
|
+
if (out) {
|
|
613
|
+
const versions = extractVersions(out);
|
|
614
|
+
if (versions.length > 0) {
|
|
615
|
+
targetVersion = pickLatestVersion(versions);
|
|
616
|
+
info(` ${dep.depKey} 线上可用版本(${versions.length} 个),选取: ${targetVersion}`);
|
|
617
|
+
} else {
|
|
618
|
+
warn(` ohpm info ${dep.depKey} 输出中未提取到版本号`);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
candidates.push({
|
|
624
|
+
pkgFile: dep.pkgFile,
|
|
625
|
+
section: dep.section,
|
|
626
|
+
depKey: dep.depKey,
|
|
627
|
+
depValue: dep.depValue,
|
|
628
|
+
matchedModule,
|
|
629
|
+
targetVersion,
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
if (candidates.length === 0) {
|
|
634
|
+
error(pkgFilter ? `未找到依赖键为 ${pkgFilter} 的 file/相对路径依赖。` : '未找到匹配的 file 依赖。');
|
|
635
|
+
process.exit(2);
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
// 4. 输出定位结果
|
|
639
|
+
info('=== 定位到本地 file 依赖 ===');
|
|
640
|
+
for (const c of candidates) {
|
|
641
|
+
const relPkg = path.relative(projectRoot, c.pkgFile) || path.basename(c.pkgFile);
|
|
642
|
+
info(` ${c.depKey} (${relPkg} ${c.section}) = ${c.depValue}`);
|
|
643
|
+
if (c.matchedModule) {
|
|
644
|
+
info(` -> 匹配本地模块: name=${c.matchedModule.name}, srcPath=${c.matchedModule.srcPath}`);
|
|
645
|
+
} else {
|
|
646
|
+
warn(` -> build-profile.json5 modules[] 中未注册该模块(仅替换依赖值)`);
|
|
647
|
+
}
|
|
648
|
+
if (c.targetVersion) {
|
|
649
|
+
info(` -> 线上版本: ${c.targetVersion}`);
|
|
650
|
+
} else {
|
|
651
|
+
warn(` -> 未获取到线上版本(需 --version 指定或检查 ohpm registry)`);
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
// 5. dry-run 提前结束
|
|
656
|
+
if (dryRun) {
|
|
657
|
+
info('=== DRY-RUN 计划改动(未写入任何文件) ===');
|
|
658
|
+
for (const c of candidates) {
|
|
659
|
+
if (!c.targetVersion) {
|
|
660
|
+
warn(` [跳过] ${c.depKey}: 无目标版本`);
|
|
661
|
+
continue;
|
|
662
|
+
}
|
|
663
|
+
const relPkg = path.relative(projectRoot, c.pkgFile) || path.basename(c.pkgFile);
|
|
664
|
+
info(` ${relPkg}: "${c.depKey}": "${c.depValue}" -> "${c.targetVersion}"`);
|
|
665
|
+
if (c.matchedModule) {
|
|
666
|
+
info(` build-profile.json5: 移除 modules[] 中 ${c.matchedModule.name}`);
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
process.exit(0);
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
// 6. 确定无版本依赖的处理
|
|
673
|
+
const noVersion = candidates.filter((c) => !c.targetVersion);
|
|
674
|
+
if (noVersion.length > 0) {
|
|
675
|
+
error(`以下依赖未获取到线上版本,中止修改(避免写入不完整改动):`);
|
|
676
|
+
for (const c of noVersion) {
|
|
677
|
+
error(` - ${c.depKey}(原值 ${c.depValue})`);
|
|
678
|
+
}
|
|
679
|
+
error('请为这些包通过 --version 指定版本号,或修正 ohpm registry 后重试。');
|
|
680
|
+
process.exit(2);
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
// 7. 执行修改
|
|
684
|
+
const changes = [];
|
|
685
|
+
let failed = false;
|
|
686
|
+
|
|
687
|
+
for (const c of candidates) {
|
|
688
|
+
// 修改 oh-package.json5
|
|
689
|
+
const pkgContent = readFileSafe(c.pkgFile);
|
|
690
|
+
if (pkgContent === null) {
|
|
691
|
+
error(`读取失败: ${c.pkgFile}`);
|
|
692
|
+
failed = true;
|
|
693
|
+
continue;
|
|
694
|
+
}
|
|
695
|
+
const newContent = replaceDependencyValue(pkgContent, c.depKey, c.targetVersion);
|
|
696
|
+
if (newContent === null || newContent === pkgContent) {
|
|
697
|
+
error(`在 ${c.pkgFile} 中未找到依赖键 ${c.depKey},无法替换`);
|
|
698
|
+
failed = true;
|
|
699
|
+
continue;
|
|
700
|
+
}
|
|
701
|
+
fs.writeFileSync(c.pkgFile, newContent, 'utf-8');
|
|
702
|
+
ok(`${path.relative(projectRoot, c.pkgFile)}: "${c.depKey}": "${c.depValue}" -> "${c.targetVersion}"`);
|
|
703
|
+
changes.push({
|
|
704
|
+
file: path.relative(projectRoot, c.pkgFile) || path.basename(c.pkgFile),
|
|
705
|
+
action: 'replace-dependency',
|
|
706
|
+
key: c.depKey,
|
|
707
|
+
oldValue: c.depValue,
|
|
708
|
+
newValue: c.targetVersion,
|
|
709
|
+
});
|
|
710
|
+
|
|
711
|
+
// 移除 build-profile.json5 中的本地模块条目
|
|
712
|
+
if (c.matchedModule && bpContent) {
|
|
713
|
+
for (const mod of modules) {
|
|
714
|
+
if (
|
|
715
|
+
mod.absStart === c.matchedModule.absStart &&
|
|
716
|
+
mod.absEnd === c.matchedModule.absEnd
|
|
717
|
+
) {
|
|
718
|
+
const removed = removeModuleFromContent(bpContent, mod);
|
|
719
|
+
if (removed !== null) {
|
|
720
|
+
fs.writeFileSync(bpPath, removed, 'utf-8');
|
|
721
|
+
ok(`build-profile.json5: 移除本地模块 ${mod.name}(srcPath=${mod.srcPath})`);
|
|
722
|
+
changes.push({
|
|
723
|
+
file: 'build-profile.json5',
|
|
724
|
+
action: 'remove-module',
|
|
725
|
+
module: mod.name,
|
|
726
|
+
});
|
|
727
|
+
} else {
|
|
728
|
+
error(`build-profile.json5 移除模块 ${mod.name} 失败(文本结构异常)`);
|
|
729
|
+
failed = true;
|
|
730
|
+
}
|
|
731
|
+
break;
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
if (failed) {
|
|
738
|
+
error('存在修改失败项,请检查上述错误后重试。已成功的修改保留不回滚,请核对后再继续。');
|
|
739
|
+
process.exit(1);
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
// 8. 可选 ohpm install
|
|
743
|
+
let installResult = null;
|
|
744
|
+
if (runInstall) {
|
|
745
|
+
info('执行 ohpm install ...');
|
|
746
|
+
try {
|
|
747
|
+
execFileSync('ohpm', ['install'], {
|
|
748
|
+
cwd: projectRoot,
|
|
749
|
+
encoding: 'utf-8',
|
|
750
|
+
shell: process.platform === 'win32',
|
|
751
|
+
timeout: 300000,
|
|
752
|
+
stdio: 'inherit',
|
|
753
|
+
});
|
|
754
|
+
installResult = { ok: true };
|
|
755
|
+
ok('ohpm install 执行成功');
|
|
756
|
+
} catch (e) {
|
|
757
|
+
installResult = { ok: false, message: String(e.message || '').slice(0, 300) };
|
|
758
|
+
error('ohpm install 执行失败,请手动在工程目录执行 ohpm install 后重新编译。');
|
|
759
|
+
error(` ${installResult.message}`);
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
// 9. 输出改动清单报告
|
|
764
|
+
const report = {
|
|
765
|
+
tool: 'ohpm-switch',
|
|
766
|
+
version: '1.0.0',
|
|
767
|
+
timestamp: new Date().toISOString(),
|
|
768
|
+
sourcePath: projectRoot,
|
|
769
|
+
dryRun: false,
|
|
770
|
+
ohpmAvailable: !!ohpmAvailable,
|
|
771
|
+
ohpmVersion: ohpmAvailable || null,
|
|
772
|
+
changes,
|
|
773
|
+
runInstall,
|
|
774
|
+
installResult,
|
|
775
|
+
};
|
|
776
|
+
const reportPath = path.join(projectRoot, 'ohpm-switch-report.json');
|
|
777
|
+
fs.writeFileSync(reportPath, JSON.stringify(report, null, 2), 'utf-8');
|
|
778
|
+
ok(`改动清单已写入: ${reportPath}`);
|
|
779
|
+
|
|
780
|
+
if (runInstall && installResult && !installResult.ok) {
|
|
781
|
+
// install 失败但文件修改已生效:返回 1 提醒用户,仍保留 report
|
|
782
|
+
process.exit(1);
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
ok('ohpm 线上版本替换完成。请使用 build.ps1 重新编译验证。');
|
|
786
|
+
process.exit(0);
|
|
787
|
+
}
|
|
788
|
+
|
|
789
789
|
main();
|