hcordova 1.0.0
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/LICENSE +204 -0
- package/README.md +346 -0
- package/bin/hcordova +27 -0
- package/package.json +39 -0
- package/src/base-cli.js +1181 -0
- package/src/utils/DependencyChecker.js +382 -0
- package/src/utils/PlatformProject.js +452 -0
- package/src/utils/PluginConfigParser.js +408 -0
- package/src/utils/PluginDownloader.js +181 -0
- package/src/utils/PluginHandler.js +1097 -0
- package/src/utils/VariableValidator.js +369 -0
- package/src/utils/args-processor.js +79 -0
- package/templates/project/AppScope/app.json5 +10 -0
- package/templates/project/AppScope/resources/base/element/string.json +8 -0
- package/templates/project/AppScope/resources/base/media/background.png +0 -0
- package/templates/project/AppScope/resources/base/media/foreground.png +0 -0
- package/templates/project/AppScope/resources/base/media/layered_image.json +7 -0
- package/templates/project/build-profile.json5 +46 -0
- package/templates/project/code-linter.json5 +32 -0
- package/templates/project/entry/build-profile.json5 +28 -0
- package/templates/project/entry/hvigorfile.ts +6 -0
- package/templates/project/entry/obfuscation-rules.txt +23 -0
- package/templates/project/entry/oh-package.json5 +12 -0
- package/templates/project/entry/src/main/ets/entryability/EntryAbility.ets +62 -0
- package/templates/project/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets +32 -0
- package/templates/project/entry/src/main/ets/pages/Index.ets +40 -0
- package/templates/project/entry/src/main/module.json5 +52 -0
- package/templates/project/entry/src/main/resources/base/element/color.json +8 -0
- package/templates/project/entry/src/main/resources/base/element/float.json +8 -0
- package/templates/project/entry/src/main/resources/base/element/string.json +16 -0
- package/templates/project/entry/src/main/resources/base/media/background.png +0 -0
- package/templates/project/entry/src/main/resources/base/media/foreground.png +0 -0
- package/templates/project/entry/src/main/resources/base/media/layered_image.json +7 -0
- package/templates/project/entry/src/main/resources/base/media/startIcon.png +0 -0
- package/templates/project/entry/src/main/resources/base/profile/backup_config.json +3 -0
- package/templates/project/entry/src/main/resources/base/profile/main_pages.json +5 -0
- package/templates/project/entry/src/main/resources/dark/element/color.json +8 -0
- package/templates/project/entry/src/main/resources/rawfile/config.xml +26 -0
- package/templates/project/entry/src/main/resources/rawfile/www/cordova.js +1925 -0
- package/templates/project/entry/src/main/resources/rawfile/www/css/index.css +158 -0
- package/templates/project/entry/src/main/resources/rawfile/www/img/cordova.png +0 -0
- package/templates/project/entry/src/main/resources/rawfile/www/img/logo.png +0 -0
- package/templates/project/entry/src/main/resources/rawfile/www/index.html +110 -0
- package/templates/project/entry/src/main/resources/rawfile/www/js/index.js +68 -0
- package/templates/project/entry/src/mock/mock-config.json5 +2 -0
- package/templates/project/entry/src/ohosTest/ets/test/Ability.test.ets +35 -0
- package/templates/project/entry/src/ohosTest/ets/test/List.test.ets +5 -0
- package/templates/project/entry/src/ohosTest/module.json5 +13 -0
- package/templates/project/entry/src/test/List.test.ets +5 -0
- package/templates/project/entry/src/test/LocalUnit.test.ets +33 -0
- package/templates/project/hvigor/hvigor-config.json5 +22 -0
- package/templates/project/hvigorfile.ts +6 -0
- package/templates/project/local.properties +9 -0
- package/templates/project/oh-package-lock.json5 +27 -0
- package/templates/project/oh-package.json5 +10 -0
package/src/base-cli.js
ADDED
|
@@ -0,0 +1,1181 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025 Huawei Device, Inc. Ltd. and <马弓手>.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const fs = require('fs');
|
|
18
|
+
const path = require('path');
|
|
19
|
+
const chalk = require('chalk');
|
|
20
|
+
const { spawn, execSync } = require('child_process');
|
|
21
|
+
|
|
22
|
+
const PlatformProject = require('./utils/PlatformProject');
|
|
23
|
+
const PluginDownloader = require('./utils/PluginDownloader');
|
|
24
|
+
const PluginHandler = require('./utils/PluginHandler');
|
|
25
|
+
const DependencyChecker = require('./utils/DependencyChecker');
|
|
26
|
+
const PluginConfigParser = require('./utils/PluginConfigParser');
|
|
27
|
+
const VariableValidator = require('./utils/VariableValidator');
|
|
28
|
+
|
|
29
|
+
const PLUGNI_DIR = "oh-plugins"
|
|
30
|
+
|
|
31
|
+
function setupEvents(externalEventEmitter) {
|
|
32
|
+
if (externalEventEmitter) {
|
|
33
|
+
return externalEventEmitter;
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
emit: (type, message) => console.log(`${type}: ${message}`)
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
class BaseCLI {
|
|
41
|
+
constructor() {
|
|
42
|
+
this.supportedPlatforms = ['android', 'ios', 'harmonyos', 'harmony', 'ohos'];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
run(args) {
|
|
46
|
+
if (args.length === 0) {
|
|
47
|
+
this.showHelp();
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const command = args[0];
|
|
52
|
+
switch (command) {
|
|
53
|
+
case '--version':
|
|
54
|
+
case '-v':
|
|
55
|
+
this.showVersion();
|
|
56
|
+
break;
|
|
57
|
+
case '--help':
|
|
58
|
+
case '-h':
|
|
59
|
+
this.showHelp();
|
|
60
|
+
break;
|
|
61
|
+
case 'create':
|
|
62
|
+
this.handleCreate(args.slice(1));
|
|
63
|
+
break;
|
|
64
|
+
case 'add':
|
|
65
|
+
this.handleAdd(args.slice(1));
|
|
66
|
+
break;
|
|
67
|
+
case 'platform':
|
|
68
|
+
this.handlePlatformCommand(args.slice(1));
|
|
69
|
+
break;
|
|
70
|
+
case 'plugin':
|
|
71
|
+
this.handlePluginCommand(args.slice(1));
|
|
72
|
+
break;
|
|
73
|
+
case 'build':
|
|
74
|
+
this.handleBuild(args.slice(1));
|
|
75
|
+
break;
|
|
76
|
+
case 'run':
|
|
77
|
+
this.handleRun(args.slice(1));
|
|
78
|
+
break;
|
|
79
|
+
case 'list':
|
|
80
|
+
case 'ls':
|
|
81
|
+
this.handleList(args.slice(1));
|
|
82
|
+
break;
|
|
83
|
+
case 'info':
|
|
84
|
+
this.handleInfo();
|
|
85
|
+
break;
|
|
86
|
+
default:
|
|
87
|
+
console.log(chalk.red('Unknown command: ' + command));
|
|
88
|
+
this.showHelp();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
handleCreate(args) {
|
|
93
|
+
if (args.length < 1) {
|
|
94
|
+
console.log(chalk.red('Please specify project path'));
|
|
95
|
+
console.log(chalk.yellow('Usage: hcordova create <path> [appId] [appName]'));
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const projectPath = args[0];
|
|
100
|
+
const id = args[1] || 'com.example.app';
|
|
101
|
+
const name = args[2] || 'MyApp';
|
|
102
|
+
|
|
103
|
+
console.log(chalk.yellow('Creating Cordova project...'));
|
|
104
|
+
console.log(' Path: ' + projectPath);
|
|
105
|
+
console.log(' App ID: ' + id);
|
|
106
|
+
console.log(' App Name: ' + name);
|
|
107
|
+
|
|
108
|
+
this.executeCordovaCommand(['create', projectPath, id, name]);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
handleAdd(args) {
|
|
112
|
+
if (args.length < 1) {
|
|
113
|
+
console.log(chalk.red('Please specify platform name'));
|
|
114
|
+
console.log(chalk.yellow('Usage: hcordova add <platform>'));
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
this.addPlatform(args.slice(0));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
handlePlatformCommand(args) {
|
|
122
|
+
if (args.length < 2) {
|
|
123
|
+
this.showPlatformHelp();
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const subcommand = args[0];
|
|
128
|
+
const subargs = args.slice(1);
|
|
129
|
+
|
|
130
|
+
switch (subcommand) {
|
|
131
|
+
case 'add':
|
|
132
|
+
if (args.length < 2) {
|
|
133
|
+
console.log(chalk.red('Please specify platform name'));
|
|
134
|
+
console.log(chalk.yellow('Usage: hcordova platform add <platform>'));
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
this.addPlatform(subargs);
|
|
138
|
+
break;
|
|
139
|
+
case 'remove':
|
|
140
|
+
case 'rm':
|
|
141
|
+
if (args.length < 2) {
|
|
142
|
+
console.log(chalk.red('Please specify platform name'));
|
|
143
|
+
console.log(chalk.yellow('Usage: hcordova platform remove <platform>'));
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
this.removePlatform(subargs);
|
|
147
|
+
break;
|
|
148
|
+
case 'list':
|
|
149
|
+
case 'ls':
|
|
150
|
+
this.listPlatforms();
|
|
151
|
+
break;
|
|
152
|
+
default:
|
|
153
|
+
console.log(chalk.red('Unknown platform subcommand: ' + subcommand));
|
|
154
|
+
this.showPlatformHelp();
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
handlePluginCommand(args) {
|
|
159
|
+
if (args.length === 0) {
|
|
160
|
+
this.showPluginHelp();
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const subcommand = args[0];
|
|
165
|
+
const subargs = args.slice(1);
|
|
166
|
+
switch (subcommand) {
|
|
167
|
+
case 'add':
|
|
168
|
+
if (args.length < 2) {
|
|
169
|
+
console.log(chalk.red('Please specify plugin name'));
|
|
170
|
+
console.log(chalk.yellow('Usage: hcordova plugin add <plugin>'));
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
this.addPlugin(subargs);
|
|
174
|
+
break;
|
|
175
|
+
case 'remove':
|
|
176
|
+
case 'rm':
|
|
177
|
+
if (args.length < 2) {
|
|
178
|
+
console.log(chalk.red('Please specify plugin name'));
|
|
179
|
+
console.log(chalk.yellow('Usage: hcordova plugin remove <plugin>'));
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
this.removePlugin(subargs);
|
|
183
|
+
break;
|
|
184
|
+
case 'list':
|
|
185
|
+
case 'ls':
|
|
186
|
+
this.listPlugins();
|
|
187
|
+
break;
|
|
188
|
+
default:
|
|
189
|
+
console.log(chalk.red('Unknown plugin subcommand: ' + subcommand));
|
|
190
|
+
this.showPluginHelp();
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
addPlatform(subargs) {
|
|
195
|
+
let platform = subargs[0];
|
|
196
|
+
const normalizedPlatform = this.normalizePlatform(platform);
|
|
197
|
+
|
|
198
|
+
if (!normalizedPlatform) {
|
|
199
|
+
console.log(chalk.red('Unsupported platform: ' + platform));
|
|
200
|
+
console.log(chalk.yellow('Supported platforms: android, ios, harmonyos'));
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
console.log(chalk.yellow('Adding ' + normalizedPlatform + ' platform...'));
|
|
205
|
+
|
|
206
|
+
if (normalizedPlatform === 'harmonyos') {
|
|
207
|
+
// 鸿蒙平台使用自定义处理
|
|
208
|
+
this.addHarmonyOSPlatform();
|
|
209
|
+
} else {
|
|
210
|
+
// Android 和 iOS 调用官方 cordova 命令
|
|
211
|
+
console.log('Using official Cordova for ' + normalizedPlatform + ' platform');
|
|
212
|
+
this.executeCordovaCommand(['platform', 'add', normalizedPlatform]);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
removePlatform(subargs) {
|
|
217
|
+
let platform = subargs[0];
|
|
218
|
+
const normalizedPlatform = this.normalizePlatform(platform);
|
|
219
|
+
|
|
220
|
+
if (!normalizedPlatform) {
|
|
221
|
+
console.log(chalk.red('Unsupported platform: ' + platform));
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
console.log(chalk.yellow('Removing ' + normalizedPlatform + ' platform...'));
|
|
226
|
+
|
|
227
|
+
if (normalizedPlatform === 'harmonyos') {
|
|
228
|
+
// 鸿蒙平台:直接删除目录
|
|
229
|
+
const platformDir = 'harmonyos';
|
|
230
|
+
if (fs.existsSync(platformDir)) {
|
|
231
|
+
fs.rmSync(platformDir, { recursive: true, force: true });
|
|
232
|
+
console.log(chalk.green('HarmonyOS platform removed successfully!'));
|
|
233
|
+
} else {
|
|
234
|
+
console.log(chalk.yellow('HarmonyOS platform is not installed.'));
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (fs.existsSync(PLUGNI_DIR)) {
|
|
238
|
+
fs.rmSync(PLUGNI_DIR, { recursive: true, force: true });
|
|
239
|
+
console.log(chalk.green('HarmonyOS plugins removed successfully!'));
|
|
240
|
+
} else {
|
|
241
|
+
console.log(chalk.yellow('HarmonyOS plugins is not installed.'));
|
|
242
|
+
}
|
|
243
|
+
} else {
|
|
244
|
+
// Android 和 iOS 调用官方 cordova 命令
|
|
245
|
+
this.executeCordovaCommand(['platform', 'remove', normalizedPlatform]);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
addPlugin(args) {
|
|
250
|
+
if (args.length < 1) {
|
|
251
|
+
this.showPluginHelp();
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const parsed = this.parsePluginInstallArgs(args);
|
|
256
|
+
|
|
257
|
+
// 验证参数
|
|
258
|
+
try {
|
|
259
|
+
this.validatePluginArgs(parsed);
|
|
260
|
+
} catch (error) {
|
|
261
|
+
console.log(chalk.red(error.message));
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
console.log(chalk.yellow(`Adding plugin: ${parsed.pluginSpec}`));
|
|
266
|
+
|
|
267
|
+
if (parsed.cli_variables.size > 0) {
|
|
268
|
+
console.log(chalk.yellow('Plugin variables:'));
|
|
269
|
+
parsed.cli_variables.forEach((value, key) => {
|
|
270
|
+
console.log(chalk.yellow(` ${key}=${value}`));
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
if (parsed.options.length > 0) {
|
|
275
|
+
console.log(chalk.yellow('Additional options: ' + parsed.options.join(' ')));
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const hasHarmonyOS = fs.existsSync('harmonyos');
|
|
279
|
+
const cordovaArgs = this.buildCordovaPluginArgs(parsed);
|
|
280
|
+
|
|
281
|
+
console.log("addPlugin start:"+hasHarmonyOS);
|
|
282
|
+
if (hasHarmonyOS) {
|
|
283
|
+
console.log('HarmonyOS platform detected, installing plugin for all platforms...');
|
|
284
|
+
console.log('Installing plugin for Android/iOS...');
|
|
285
|
+
if(parsed.platform == "harmonyos") {
|
|
286
|
+
console.log('Installing plugin for HarmonyOS...');
|
|
287
|
+
this.installPluginForHarmonyOS(parsed);
|
|
288
|
+
} else {
|
|
289
|
+
this.executeCordovaCommand(cordovaArgs, () => {
|
|
290
|
+
console.log('Installing plugin for HarmonyOS...');
|
|
291
|
+
this.installPluginForHarmonyOS(parsed);
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
} else {
|
|
295
|
+
this.executeCordovaCommand(cordovaArgs);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// 参数验证方法
|
|
300
|
+
validatePluginArgs(parsed) {
|
|
301
|
+
const errors = [];
|
|
302
|
+
|
|
303
|
+
// 验证插件规格
|
|
304
|
+
if (!parsed.pluginSpec || parsed.pluginSpec.trim() === '') {
|
|
305
|
+
errors.push('Plugin specification cannot be empty');
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// 验证变量格式
|
|
309
|
+
parsed.cli_variables.forEach((value, key) => {
|
|
310
|
+
if (!key || key.trim() === '') {
|
|
311
|
+
errors.push('Variable name cannot be empty');
|
|
312
|
+
}
|
|
313
|
+
if (value === undefined || value === null) {
|
|
314
|
+
errors.push(`Variable ${key} must have a value`);
|
|
315
|
+
}
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
if (errors.length > 0) {
|
|
319
|
+
throw new Error('Invalid plugin arguments:\n' + errors.map(err => ` - ${err}`).join('\n'));
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// 解析插件安装参数
|
|
324
|
+
parsePluginInstallArgs(args) {
|
|
325
|
+
const result = {
|
|
326
|
+
pluginSpec: args[0],
|
|
327
|
+
cli_variables: new Map(),
|
|
328
|
+
options: [],
|
|
329
|
+
platform: null, // 新增:支持的平台列表
|
|
330
|
+
searchPath: null,
|
|
331
|
+
noregistry: false,
|
|
332
|
+
link: false,
|
|
333
|
+
force: false,
|
|
334
|
+
save: true
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
for (let i = 1; i < args.length; i++) {
|
|
338
|
+
const arg = args[i];
|
|
339
|
+
|
|
340
|
+
if (arg.startsWith('--variable=')) {
|
|
341
|
+
const [name, value] = arg.substring(11).split('=', 2);
|
|
342
|
+
if (name && value !== undefined) {
|
|
343
|
+
result.cli_variables.set(name, value);
|
|
344
|
+
}
|
|
345
|
+
} else if (arg === '--variable' && i + 1 < args.length) {
|
|
346
|
+
const [name, value] = args[++i].split('=', 2);
|
|
347
|
+
if (name && value !== undefined) {
|
|
348
|
+
result.cli_variables.set(name, value);
|
|
349
|
+
}
|
|
350
|
+
} else if (arg === '--searchpath' && i + 1 < args.length) {
|
|
351
|
+
result.searchPath = args[++i];
|
|
352
|
+
} else if (arg === '--noregistry') {
|
|
353
|
+
result.noregistry = true;
|
|
354
|
+
} else if (arg === '--link') {
|
|
355
|
+
result.link = true;
|
|
356
|
+
} else if (arg === '--force') {
|
|
357
|
+
result.force = true;
|
|
358
|
+
} else if (arg === '--nosave') {
|
|
359
|
+
result.save = false;
|
|
360
|
+
} else if (arg === '--platform' && i + 1 < args.length) {
|
|
361
|
+
const platformArg = args[++i].toLowerCase();
|
|
362
|
+
result.platform = this.normalizePlatform(platformArg);
|
|
363
|
+
} else if (arg.startsWith('--platform=')) {
|
|
364
|
+
const platformArg = arg.substring(11).toLowerCase();
|
|
365
|
+
result.platform = this.normalizePlatform(platformArg);
|
|
366
|
+
} else if (arg.startsWith('-')) {
|
|
367
|
+
result.options.push(arg);
|
|
368
|
+
} else {
|
|
369
|
+
// 可能是本地路径或版本号
|
|
370
|
+
result.options.push(arg);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
return result;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
// 构建 Cordova 命令参数
|
|
379
|
+
buildCordovaPluginArgs(parsed) {
|
|
380
|
+
const args = ['plugin', 'add', parsed.pluginSpec];
|
|
381
|
+
|
|
382
|
+
// 添加变量参数
|
|
383
|
+
parsed.cli_variables.forEach((value, name) => {
|
|
384
|
+
args.push('--variable', `${name}=${value}`);
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
// 添加其他选项
|
|
388
|
+
if (parsed.searchPath) {
|
|
389
|
+
args.push('--searchpath', parsed.searchPath);
|
|
390
|
+
}
|
|
391
|
+
if (parsed.noregistry) {
|
|
392
|
+
args.push('--noregistry');
|
|
393
|
+
}
|
|
394
|
+
if (parsed.link) {
|
|
395
|
+
args.push('--link');
|
|
396
|
+
}
|
|
397
|
+
if (parsed.force) {
|
|
398
|
+
args.push('--force');
|
|
399
|
+
}
|
|
400
|
+
if (!parsed.save) {
|
|
401
|
+
args.push('--nosave');
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// 添加其他选项
|
|
405
|
+
args.push(...parsed.options);
|
|
406
|
+
|
|
407
|
+
return args;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
removePlugin(args) {
|
|
411
|
+
if (args.length < 1) {
|
|
412
|
+
this.showPluginRemoveHelp();
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
const parsed = this.parsePluginRemoveArgs(args);
|
|
417
|
+
|
|
418
|
+
console.log(chalk.yellow(`Removing plugin: ${parsed.pluginSpec}`));
|
|
419
|
+
|
|
420
|
+
// 显示变量信息
|
|
421
|
+
if (parsed.cli_variables.size > 0) {
|
|
422
|
+
console.log(chalk.yellow('Plugin variables:'));
|
|
423
|
+
parsed.cli_variables.forEach((value, key) => {
|
|
424
|
+
console.log(chalk.yellow(` ${key}=${value}`));
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
if (parsed.options.length > 0) {
|
|
429
|
+
console.log(chalk.yellow('Removal options: ' + parsed.options.join(' ')));
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
const hasHarmonyOS = fs.existsSync('harmonyos');
|
|
433
|
+
const cordovaArgs = this.buildCordovaPluginRemoveArgs(parsed);
|
|
434
|
+
|
|
435
|
+
if (hasHarmonyOS) {
|
|
436
|
+
console.log(chalk.yellow('HarmonyOS platform detected, removing plugin from all platforms...'));
|
|
437
|
+
|
|
438
|
+
console.log(chalk.yellow('Removing plugin from HarmonyOS...'));
|
|
439
|
+
this.removePluginFromHarmonyOS(parsed, () => {
|
|
440
|
+
console.log(chalk.yellow('Removing plugin from Android/iOS...'));
|
|
441
|
+
this.executeCordovaCommand(cordovaArgs);
|
|
442
|
+
});
|
|
443
|
+
} else {
|
|
444
|
+
this.executeCordovaCommand(cordovaArgs);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// 构建 Cordova 移除命令参数
|
|
449
|
+
buildCordovaPluginRemoveArgs(parsed) {
|
|
450
|
+
const args = ['plugin', 'remove', parsed.pluginSpec];
|
|
451
|
+
|
|
452
|
+
// 添加变量参数
|
|
453
|
+
parsed.cli_variables.forEach((value, name) => {
|
|
454
|
+
args.push('--variable', `${name}=${value}`);
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
// 添加选项
|
|
458
|
+
if (!parsed.save) {
|
|
459
|
+
args.push('--nosave');
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
args.push(...parsed.options);
|
|
463
|
+
|
|
464
|
+
return args;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// 解析插件移除参数
|
|
468
|
+
parsePluginRemoveArgs(args) {
|
|
469
|
+
const result = {
|
|
470
|
+
pluginSpec: args[0],
|
|
471
|
+
cli_variables: new Map(), // 添加变量存储
|
|
472
|
+
options: [],
|
|
473
|
+
save: true
|
|
474
|
+
};
|
|
475
|
+
|
|
476
|
+
for (let i = 1; i < args.length; i++) {
|
|
477
|
+
const arg = args[i];
|
|
478
|
+
|
|
479
|
+
if (arg === '--nosave') {
|
|
480
|
+
result.save = false;
|
|
481
|
+
} else if (arg === '--force') {
|
|
482
|
+
result.options.push('--force');
|
|
483
|
+
} else if (arg.startsWith('--variable=')) {
|
|
484
|
+
// 处理 --variable=NAME=VALUE 格式
|
|
485
|
+
const [name, value] = arg.substring(11).split('=', 2);
|
|
486
|
+
if (name && value !== undefined) {
|
|
487
|
+
result.cli_variables.set(name, value);
|
|
488
|
+
}
|
|
489
|
+
} else if (arg === '--variable' && i + 1 < args.length) {
|
|
490
|
+
// 处理 --variable NAME=VALUE 格式
|
|
491
|
+
const [name, value] = args[++i].split('=', 2);
|
|
492
|
+
if (name && value !== undefined) {
|
|
493
|
+
result.cli_variables.set(name, value);
|
|
494
|
+
}
|
|
495
|
+
} else if (arg.startsWith('-')) {
|
|
496
|
+
result.options.push(arg);
|
|
497
|
+
} else {
|
|
498
|
+
console.log(chalk.yellow(`Warning: Ignoring additional argument: ${arg}`));
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
return result;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* 动态设置项目路径
|
|
507
|
+
*/
|
|
508
|
+
_setupLocations() {
|
|
509
|
+
this.sourceRoot = path.resolve(process.cwd(), 'harmonyos');
|
|
510
|
+
|
|
511
|
+
const buildProfilePath = path.join(this.sourceRoot, 'build-profile.json5');
|
|
512
|
+
let moduleName = 'entry'; // 默认值
|
|
513
|
+
let moduleSrcPath = './entry'; // 默认路径
|
|
514
|
+
let foundModule = false;
|
|
515
|
+
|
|
516
|
+
try {
|
|
517
|
+
if (fs.existsSync(buildProfilePath)) {
|
|
518
|
+
const buildProfileContent = fs.readFileSync(buildProfilePath, 'utf8');
|
|
519
|
+
|
|
520
|
+
// 使用 json5 库解析配置文件
|
|
521
|
+
const json5 = require('json5');
|
|
522
|
+
const buildProfile = json5.parse(buildProfileContent);
|
|
523
|
+
|
|
524
|
+
// 查找 targets 中 name 为 'default' 的 module
|
|
525
|
+
if (buildProfile.modules && Array.isArray(buildProfile.modules)) {
|
|
526
|
+
// 首先尝试查找 default target
|
|
527
|
+
for (const module of buildProfile.modules) {
|
|
528
|
+
if (module.targets && Array.isArray(module.targets)) {
|
|
529
|
+
const defaultTarget = module.targets.find(target =>
|
|
530
|
+
target.name && target.name === 'default'
|
|
531
|
+
);
|
|
532
|
+
|
|
533
|
+
if (defaultTarget) {
|
|
534
|
+
moduleName = module.name;
|
|
535
|
+
moduleSrcPath = module.srcPath || `./${module.name}`;
|
|
536
|
+
this.events.emit('log', `Found default target in module: ${moduleName} with srcPath: ${moduleSrcPath}`);
|
|
537
|
+
foundModule = true;
|
|
538
|
+
break;
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// 如果没有找到 default target,使用第一个 module
|
|
544
|
+
if (!foundModule && buildProfile.modules.length > 0) {
|
|
545
|
+
const firstModule = buildProfile.modules[0];
|
|
546
|
+
moduleName = firstModule.name;
|
|
547
|
+
moduleSrcPath = firstModule.srcPath || `./${firstModule.name}`;
|
|
548
|
+
|
|
549
|
+
// 尝试使用第一个 module 的第一个 target
|
|
550
|
+
let targetName = 'unknown';
|
|
551
|
+
if (firstModule.targets && firstModule.targets.length > 0) {
|
|
552
|
+
targetName = firstModule.targets[0].name || 'unknown';
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
this.events.emit('log', `No default target found, using first module: ${moduleName} with target: ${targetName}, srcPath: ${moduleSrcPath}`);
|
|
556
|
+
foundModule = true;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
if (!foundModule) {
|
|
560
|
+
this.events.emit('warn', 'No modules with targets found in build-profile.json5, using default module "entry"');
|
|
561
|
+
}
|
|
562
|
+
} else {
|
|
563
|
+
this.events.emit('warn', 'No modules configuration found in build-profile.json5, using default module "entry"');
|
|
564
|
+
}
|
|
565
|
+
} else {
|
|
566
|
+
this.events.emit('warn', `build-profile.json5 not found at ${buildProfilePath}, using default module "entry"`);
|
|
567
|
+
}
|
|
568
|
+
} catch (error) {
|
|
569
|
+
this.events.emit('warn', `Failed to parse build-profile.json5: ${error.message}, using default module "entry"`);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
// 处理相对路径,确保路径正确
|
|
573
|
+
const moduleAbsolutePath = moduleSrcPath.startsWith('./')
|
|
574
|
+
? path.join(this.sourceRoot, moduleSrcPath.substring(2))
|
|
575
|
+
: path.join(this.sourceRoot, moduleSrcPath);
|
|
576
|
+
|
|
577
|
+
return {
|
|
578
|
+
root: this.sourceRoot,
|
|
579
|
+
www: path.join(moduleAbsolutePath, 'src', 'main', 'resources', 'rawfile'),
|
|
580
|
+
configXml: path.join(moduleAbsolutePath, 'src', 'main', 'resources', 'rawfile', "config.xml"),
|
|
581
|
+
moduleName: moduleName,
|
|
582
|
+
moduleRoot: moduleAbsolutePath,
|
|
583
|
+
moduleSrcPath: moduleSrcPath,
|
|
584
|
+
events:this.events,
|
|
585
|
+
// 添加其他常用路径
|
|
586
|
+
cordovaHarPathCpp:path.join(this.sourceRoot, 'cordova', 'src', 'main', 'cpp'),
|
|
587
|
+
cordovaHarPathEts:path.join(this.sourceRoot, 'cordova', 'src', 'main', 'ets')
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
/**
|
|
592
|
+
* 获取当前cordovaHarmony的版本号
|
|
593
|
+
*/
|
|
594
|
+
_getCordovaHarmoneyVersion() {
|
|
595
|
+
const cordovaPackagePath = path.join(this.sourceRoot, 'cordova', 'oh-package.json5');
|
|
596
|
+
this.cordovaHarmonyVersion = "1.0.0";
|
|
597
|
+
|
|
598
|
+
try {
|
|
599
|
+
if (fs.existsSync(cordovaPackagePath)) {
|
|
600
|
+
const packageContent = fs.readFileSync(cordovaPackagePath, 'utf8');
|
|
601
|
+
const json5 = require('json5');
|
|
602
|
+
const cordovaPackage = json5.parse(packageContent);
|
|
603
|
+
if(cordovaPackage.version) {
|
|
604
|
+
this.cordovaHarmonyVersion = cordovaPackage.version;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
}catch (error) {
|
|
608
|
+
this.events.emit('warn', `Failed to parse cordova oh-package.json5: ${error.message}"`);
|
|
609
|
+
process.exit(1);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
/**
|
|
614
|
+
* 记录变量报告
|
|
615
|
+
*/
|
|
616
|
+
_logVariableReport(report) {
|
|
617
|
+
this.events.emit('log', `Variable summary for ${report.plugin.name}:`);
|
|
618
|
+
this.events.emit('log', ` Total variables: ${report.summary.total}`);
|
|
619
|
+
this.events.emit('log', ` Required variables: ${report.summary.required}`);
|
|
620
|
+
this.events.emit('log', ` Provided values: ${report.summary.provided}`);
|
|
621
|
+
this.events.emit('log', ` Using defaults: ${report.summary.usingDefault}`);
|
|
622
|
+
|
|
623
|
+
// 记录详细变量信息
|
|
624
|
+
report.variables.forEach(variable => {
|
|
625
|
+
const statusIcon = variable.status === 'provided' ? '✓' :
|
|
626
|
+
variable.status === 'default' ? '○' : '✗';
|
|
627
|
+
this.events.emit('verbose',
|
|
628
|
+
` ${statusIcon} ${variable.name} = ${variable.value} ${variable.required ? '(required)' : ''}`
|
|
629
|
+
);
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* 安装插件文件
|
|
635
|
+
*/
|
|
636
|
+
async _installPluginFiles(pluginConfig, pluginDir, installOptions) {
|
|
637
|
+
this.events.emit('log', 'Installing plugin files...');
|
|
638
|
+
|
|
639
|
+
const plugin = {
|
|
640
|
+
id: pluginConfig.id,
|
|
641
|
+
package:pluginConfig.package,
|
|
642
|
+
dir: pluginDir
|
|
643
|
+
};
|
|
644
|
+
|
|
645
|
+
// 使用PluginHandler安装各种类型的文件
|
|
646
|
+
// 安装source-file
|
|
647
|
+
for (const sourceFile of pluginConfig.platform.sourceFiles) {
|
|
648
|
+
const installer = PluginHandler.getInstaller('source-file');
|
|
649
|
+
if (installer) {
|
|
650
|
+
installer(sourceFile, plugin, this.locations, installOptions);
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
//安装cmake-config
|
|
655
|
+
for(const para of pluginConfig.platform.cmakeConfigs) {
|
|
656
|
+
const installer = PluginHandler.getInstaller('cmake-config');
|
|
657
|
+
if (installer) {
|
|
658
|
+
installer(para, plugin, this.locations, installOptions);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
// 安装js-module
|
|
663
|
+
for (const jsModule of pluginConfig.platform.jsModules) {
|
|
664
|
+
const installer = PluginHandler.getInstaller('js-module');
|
|
665
|
+
if (installer) {
|
|
666
|
+
installer(jsModule, plugin, this.locations, installOptions);
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
// 安装config-file(传递变量)
|
|
671
|
+
for (const configFile of pluginConfig.platform.configFiles) {
|
|
672
|
+
const installer = PluginHandler.getInstaller('config-file');
|
|
673
|
+
if (installer) {
|
|
674
|
+
installer(configFile, plugin, this.locations, installOptions);
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
this.events.emit('log', 'Plugin files installed successfully');
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
// 更新鸿蒙插件安装以支持参数(预留实现)
|
|
682
|
+
async installPluginForHarmonyOS(parsed) {
|
|
683
|
+
let pluginInfo = {
|
|
684
|
+
id: parsed.pluginSpec
|
|
685
|
+
};
|
|
686
|
+
this.events = setupEvents(null);
|
|
687
|
+
this.events.emit('log', `Adding plugin ${pluginInfo.id} to HarmonyOS project...`);
|
|
688
|
+
const installOptions = parsed;
|
|
689
|
+
// 动态获取 module name 并设置 locations
|
|
690
|
+
this.locations = this._setupLocations();
|
|
691
|
+
this._getCordovaHarmoneyVersion();
|
|
692
|
+
|
|
693
|
+
try {
|
|
694
|
+
// 1. 检查是否已安装
|
|
695
|
+
const oldPluginDir = this._findPluginDirectory(pluginInfo.id);
|
|
696
|
+
if (oldPluginDir) {
|
|
697
|
+
this.events.emit(chalk.yellow('Warn'), chalk.yellow(`Update plugin ${pluginInfo.id}`));
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
// 2. 下载插件
|
|
701
|
+
const downloader = new PluginDownloader(this);
|
|
702
|
+
const pluginsDir = path.join(process.cwd(), PLUGNI_DIR);
|
|
703
|
+
const pluginDir = await downloader.download(pluginInfo.id, pluginsDir, installOptions);
|
|
704
|
+
|
|
705
|
+
// 3. 解析插件配置
|
|
706
|
+
const configParser = new PluginConfigParser(this);
|
|
707
|
+
const pluginPackage = configParser.readPluginPackage(pluginDir);
|
|
708
|
+
const pluginConfig = configParser.parse(pluginDir, pluginInfo.id, pluginPackage);
|
|
709
|
+
for(let i=0; i<pluginConfig.dependencies.length; i++) {
|
|
710
|
+
const isExist = this._findPluginDirectory(pluginConfig.dependencies[i].id);
|
|
711
|
+
if(isExist) {
|
|
712
|
+
continue;
|
|
713
|
+
}
|
|
714
|
+
let depPluginInfo = {id:pluginConfig.dependencies[i].id, name:pluginConfig.dependencies[i].id};
|
|
715
|
+
await addPlugin(depPluginInfo, installOptions);
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
// 4. 检查插件依赖
|
|
719
|
+
const dependencyChecker = new DependencyChecker(this);
|
|
720
|
+
const projectInfo = dependencyChecker.getProjectInfo(this.cordovaHarmonyVersion);
|
|
721
|
+
|
|
722
|
+
const dependencyResults = dependencyChecker.checkDependencies(pluginPackage, projectInfo);
|
|
723
|
+
|
|
724
|
+
if (!dependencyResults.satisfied) {
|
|
725
|
+
throw new Error(`Dependency check failed:\n${dependencyResults.errors.join('\n')}`);
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
// 输出警告信息
|
|
729
|
+
if (dependencyResults.warnings.length > 0) {
|
|
730
|
+
this.events.emit('warn', `Dependency warnings:\n${dependencyResults.warnings.join('\n')}`);
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
// 5. 验证和收集变量
|
|
734
|
+
const variableValidator = new VariableValidator(this);
|
|
735
|
+
const variables = variableValidator.collectAndValidateVariables(pluginConfig, installOptions);
|
|
736
|
+
|
|
737
|
+
// 6. 生成变量报告
|
|
738
|
+
const variableReport = variableValidator.generateVariableReport(pluginConfig, variables);
|
|
739
|
+
this._logVariableReport(variableReport);
|
|
740
|
+
|
|
741
|
+
|
|
742
|
+
// 7. 安装插件文件(传递变量)
|
|
743
|
+
await this._installPluginFiles(pluginConfig, pluginDir, {
|
|
744
|
+
...installOptions,
|
|
745
|
+
variables: variables
|
|
746
|
+
});
|
|
747
|
+
|
|
748
|
+
this.events.emit('log', `Successfully added plugin: ${pluginInfo.id}`);
|
|
749
|
+
return true;
|
|
750
|
+
} catch (error) {
|
|
751
|
+
this.events.emit('error', `Failed to add plugin ${pluginInfo.id}: ${error.message}`);
|
|
752
|
+
throw error;
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
/**
|
|
757
|
+
* 查找插件目录
|
|
758
|
+
*/
|
|
759
|
+
_findPluginDirectory(pluginId) {
|
|
760
|
+
const pluginsDir = path.join(process.cwd(), PLUGNI_DIR);
|
|
761
|
+
const possibleDirs = [
|
|
762
|
+
path.join(pluginsDir, pluginId),
|
|
763
|
+
path.join(pluginsDir, this._extractPluginName(pluginId))
|
|
764
|
+
];
|
|
765
|
+
|
|
766
|
+
for (const dir of possibleDirs) {
|
|
767
|
+
if (fs.existsSync(dir)) {
|
|
768
|
+
return dir;
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
return null;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
/**
|
|
775
|
+
* 从插件ID中提取名称
|
|
776
|
+
*/
|
|
777
|
+
_extractPluginName(pluginId) {
|
|
778
|
+
// 处理git URL
|
|
779
|
+
if (pluginId.includes('.git')) {
|
|
780
|
+
const match = pluginId.match(/\/([^\/]+)\.git$/);
|
|
781
|
+
if (match) return match[1];
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
// 处理普通URL
|
|
785
|
+
if (pluginId.includes('/')) {
|
|
786
|
+
const parts = pluginId.split('/');
|
|
787
|
+
return parts[parts.length - 1];
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
// 处理npm包名(可能包含scope)
|
|
791
|
+
return pluginId.replace(/^@/, '').replace(/\//g, '-');
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
// 从鸿蒙平台移除插件(预留实现)
|
|
795
|
+
async removePluginFromHarmonyOS(parsed, callback) {
|
|
796
|
+
let pluginInfo = {
|
|
797
|
+
id: parsed.pluginSpec
|
|
798
|
+
};
|
|
799
|
+
this.events = setupEvents(null);
|
|
800
|
+
this.events.emit('log', `Removing plugin ${pluginInfo.id} from HarmonyOS project...`);
|
|
801
|
+
const installOptions = parsed;
|
|
802
|
+
|
|
803
|
+
// 动态获取 module name 并设置 locations
|
|
804
|
+
this.locations = this._setupLocations();
|
|
805
|
+
this._getCordovaHarmoneyVersion();
|
|
806
|
+
//this.events.emit('verbose', `locations: ${JSON.stringify(this.locations)} with target: ${this.cordovaHarmonyVersion}, srcPath: ${this.sourceRoot}`);
|
|
807
|
+
|
|
808
|
+
try {
|
|
809
|
+
// 1. 查找插件目录
|
|
810
|
+
const pluginDir = this._findPluginDirectory(pluginInfo.id);
|
|
811
|
+
if (!pluginDir) {
|
|
812
|
+
this.events.emit('log', `Plugin ${pluginInfo.id} not found`);
|
|
813
|
+
return false;
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
// 3. 解析插件配置
|
|
817
|
+
const configParser = new PluginConfigParser(this);
|
|
818
|
+
const pluginPackage = configParser.readPluginPackage(pluginDir);
|
|
819
|
+
const pluginConfig = configParser.parse(pluginDir, pluginInfo.id, pluginPackage);
|
|
820
|
+
|
|
821
|
+
|
|
822
|
+
// 4. 验证和收集变量
|
|
823
|
+
const variableValidator = new VariableValidator(this);
|
|
824
|
+
const variables = variableValidator.collectAndValidateVariables(pluginConfig, installOptions);
|
|
825
|
+
|
|
826
|
+
// 5. 生成变量报告
|
|
827
|
+
const variableReport = variableValidator.generateVariableReport(pluginConfig, variables);
|
|
828
|
+
this._logVariableReport(variableReport);
|
|
829
|
+
|
|
830
|
+
// 4. 卸载插件文件(传递变量)
|
|
831
|
+
await this._uninstallPluginFiles(pluginConfig, pluginDir, {
|
|
832
|
+
...installOptions,
|
|
833
|
+
variables: variables
|
|
834
|
+
});
|
|
835
|
+
|
|
836
|
+
// 6. 删除插件目录
|
|
837
|
+
this._removePluginDirectory(pluginDir);
|
|
838
|
+
|
|
839
|
+
this.events.emit('log', `Successfully removed plugin: ${pluginInfo.id}`);
|
|
840
|
+
return true;
|
|
841
|
+
|
|
842
|
+
} catch (error) {
|
|
843
|
+
this.events.emit('error', `Failed to remove plugin ${pluginInfo.id}: ${error.message}`);
|
|
844
|
+
throw error;
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
/**
|
|
849
|
+
* 删除插件目录
|
|
850
|
+
*/
|
|
851
|
+
_removePluginDirectory(pluginDir) {
|
|
852
|
+
try {
|
|
853
|
+
if (fs.existsSync(pluginDir)) {
|
|
854
|
+
fs.rmSync(pluginDir, { recursive: true, force: true });
|
|
855
|
+
this.events.emit('verbose', `Removed plugin directory: ${pluginDir}`);
|
|
856
|
+
}
|
|
857
|
+
} catch (error) {
|
|
858
|
+
this.events.emit('warn', `Failed to remove plugin directory ${pluginDir}: ${error.message}`);
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
/**
|
|
863
|
+
* 卸载插件文件
|
|
864
|
+
*/
|
|
865
|
+
async _uninstallPluginFiles(pluginConfig, pluginDir, uninstallOptions) {
|
|
866
|
+
this.events.emit('log', 'Uninstalling plugin files...');
|
|
867
|
+
|
|
868
|
+
const plugin = {
|
|
869
|
+
id: pluginConfig.id,
|
|
870
|
+
package:pluginConfig.package,
|
|
871
|
+
dir: pluginDir
|
|
872
|
+
};
|
|
873
|
+
|
|
874
|
+
// 使用PluginHandler卸载各种类型的文件
|
|
875
|
+
// 卸载source-file
|
|
876
|
+
for (const sourceFile of pluginConfig.platform.sourceFiles) {
|
|
877
|
+
const uninstaller = PluginHandler.getUninstaller('source-file');
|
|
878
|
+
if (uninstaller) {
|
|
879
|
+
uninstaller(sourceFile, plugin, this.locations, uninstallOptions);
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
// 卸载make-file
|
|
884
|
+
for (const para of pluginConfig.platform.cmakeConfigs) {
|
|
885
|
+
const uninstaller = PluginHandler.getUninstaller('cmake-config');
|
|
886
|
+
if (uninstaller) {
|
|
887
|
+
uninstaller(para, plugin, this.locations, uninstallOptions);
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
// 卸载js-module
|
|
892
|
+
for (const jsModule of pluginConfig.platform.jsModules) {
|
|
893
|
+
const uninstaller = PluginHandler.getUninstaller('js-module');
|
|
894
|
+
if (uninstaller) {
|
|
895
|
+
uninstaller(jsModule, plugin, this.locations, uninstallOptions);
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
// 卸载config-file(传递变量)
|
|
900
|
+
for (const configFile of pluginConfig.platform.configFiles) {
|
|
901
|
+
const uninstaller = PluginHandler.getUninstaller('config-file');
|
|
902
|
+
if (uninstaller) {
|
|
903
|
+
uninstaller(configFile, plugin, this.locations, uninstallOptions);
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
this.events.emit('log', 'Plugin files uninstalled successfully');
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
listPlatforms() {
|
|
911
|
+
console.log(chalk.yellow('Checking installed platforms...'));
|
|
912
|
+
|
|
913
|
+
// 先调用官方 cordova 列出平台
|
|
914
|
+
this.executeCordovaCommand(['platform', 'list']);
|
|
915
|
+
// 检查是否有鸿蒙平台
|
|
916
|
+
if (fs.existsSync('harmonyos')) {
|
|
917
|
+
console.log('harmonyos (installed)');
|
|
918
|
+
} else {
|
|
919
|
+
console.log('harmonyos (not installed)');
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
// 修改 listPlugins 方法以显示鸿蒙插件状态
|
|
924
|
+
listPlugins() {
|
|
925
|
+
console.log(chalk.yellow('Checking installed plugins...'));
|
|
926
|
+
|
|
927
|
+
// 调用官方 cordova 列出插件
|
|
928
|
+
this.executeCordovaCommand(['plugin', 'list'], () => {
|
|
929
|
+
// 检查是否有鸿蒙平台
|
|
930
|
+
const hasHarmonyOS = fs.existsSync('platforms/harmonyos');
|
|
931
|
+
if (hasHarmonyOS) {
|
|
932
|
+
console.log(chalk.yellow('\nHarmonyOS plugins status:'));
|
|
933
|
+
console.log(' All installed plugins are available for HarmonyOS');
|
|
934
|
+
console.log(' (HarmonyOS plugin integration is pending implementation)');
|
|
935
|
+
}
|
|
936
|
+
});
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
handleBuild(args) {
|
|
940
|
+
if (args.length < 1) {
|
|
941
|
+
console.log(chalk.red('Please specify platform name'));
|
|
942
|
+
console.log(chalk.yellow('Usage: hcordova build <platform>'));
|
|
943
|
+
return;
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
const platform = args[0].toLowerCase();
|
|
947
|
+
const normalizedPlatform = this.normalizePlatform(platform);
|
|
948
|
+
|
|
949
|
+
if (!normalizedPlatform) {
|
|
950
|
+
console.log(chalk.red('Unsupported platform: ' + platform));
|
|
951
|
+
return;
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
if (normalizedPlatform === 'harmonyos') {
|
|
955
|
+
// 鸿蒙平台使用自定义构建
|
|
956
|
+
console.log(chalk.green('Please use the "hvigorw" command tool of HarmonyOS to build app'));
|
|
957
|
+
} else {
|
|
958
|
+
// Android 和 iOS 调用官方 cordova 命令
|
|
959
|
+
this.executeCordovaCommand(['build', normalizedPlatform]);
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
handleRun(args) {
|
|
964
|
+
if (args.length < 1) {
|
|
965
|
+
console.log(chalk.red('Please specify platform name'));
|
|
966
|
+
console.log(chalk.yellow('Usage: hcordova run <platform>'));
|
|
967
|
+
return;
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
const platform = args[0].toLowerCase();
|
|
971
|
+
const normalizedPlatform = this.normalizePlatform(platform);
|
|
972
|
+
|
|
973
|
+
if (!normalizedPlatform) {
|
|
974
|
+
console.log(chalk.red('Unsupported platform: ' + platform));
|
|
975
|
+
return;
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
if (normalizedPlatform === 'harmonyos') {
|
|
979
|
+
// 鸿蒙平台使用自定义运行
|
|
980
|
+
console.log(chalk.green('Please use DevEco to run and compile the app'));
|
|
981
|
+
} else {
|
|
982
|
+
// Android 和 iOS 调用官方 cordova 命令
|
|
983
|
+
this.executeCordovaCommand(['run', normalizedPlatform]);
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
handleList(args) {
|
|
988
|
+
if (args.length === 0) {
|
|
989
|
+
// 如果没有指定类型,默认列出平台
|
|
990
|
+
this.listPlatforms();
|
|
991
|
+
} else if (args[0] === 'platforms') {
|
|
992
|
+
this.listPlatforms();
|
|
993
|
+
} else if (args[0] === 'plugins') {
|
|
994
|
+
this.listPlugins();
|
|
995
|
+
} else {
|
|
996
|
+
console.log(chalk.red('Unknown list type: ' + args[0]));
|
|
997
|
+
console.log(chalk.yellow('Usage: hcordova list [platforms|plugins]'));
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
handleInfo() {
|
|
1002
|
+
console.log(chalk.yellow('Cordova Project Information:'));
|
|
1003
|
+
|
|
1004
|
+
// 调用官方 cordova info 命令
|
|
1005
|
+
try {
|
|
1006
|
+
execSync('cordova info', { stdio: 'inherit' });
|
|
1007
|
+
} catch (error) {
|
|
1008
|
+
// 如果 cordova info 失败,显示基本信息
|
|
1009
|
+
console.log(' hcordova: 1.0.0');
|
|
1010
|
+
console.log(' Node.js: ' + process.version);
|
|
1011
|
+
console.log(' OS: ' + process.platform);
|
|
1012
|
+
console.log('\n Platforms:');
|
|
1013
|
+
console.log(' Android (via Cordova)');
|
|
1014
|
+
console.log(' iOS (via Cordova)');
|
|
1015
|
+
console.log(' HarmonyOS (custom)');
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
|
|
1020
|
+
// 执行官方 cordova 命令
|
|
1021
|
+
executeCordovaCommand(args, callback) {
|
|
1022
|
+
try {
|
|
1023
|
+
console.log(chalk.gray('Executing: cordova ' + args.join(' ')));
|
|
1024
|
+
|
|
1025
|
+
const result = spawn('cordova', args, {
|
|
1026
|
+
stdio: 'inherit',
|
|
1027
|
+
shell: true
|
|
1028
|
+
});
|
|
1029
|
+
|
|
1030
|
+
result.on('close', (code) => {
|
|
1031
|
+
if (code !== 0) {
|
|
1032
|
+
console.log(chalk.red('Cordova command exited with code: ' + code));
|
|
1033
|
+
}
|
|
1034
|
+
// 命令执行完成后调用回调
|
|
1035
|
+
if (callback) callback();
|
|
1036
|
+
});
|
|
1037
|
+
|
|
1038
|
+
} catch (error) {
|
|
1039
|
+
console.log(chalk.red('Failed to execute cordova command: ' + error.message));
|
|
1040
|
+
if (callback) callback();
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
normalizePlatform(platform) {
|
|
1045
|
+
const plat = platform.toLowerCase();
|
|
1046
|
+
if (['harmonyos', 'harmony', 'ohos', 'hm'].includes(plat)) {
|
|
1047
|
+
return 'harmonyos';
|
|
1048
|
+
} else if (['android', 'ios'].includes(plat)) {
|
|
1049
|
+
return plat;
|
|
1050
|
+
}
|
|
1051
|
+
return null;
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
showVersion() {
|
|
1055
|
+
console.log('hcordova 1.0.0');
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
|
|
1059
|
+
//////////////////////////////////////////////////////////////////////////////////
|
|
1060
|
+
|
|
1061
|
+
// 自定义鸿蒙平台实现 - 主工程模板 + 动态下载 HAR 源码
|
|
1062
|
+
async addHarmonyOSPlatform() {
|
|
1063
|
+
console.log(chalk.green('Using hcordova HarmonyOS platform support'));
|
|
1064
|
+
const platform = "harmonyos"
|
|
1065
|
+
const configXmlPath = path.join(process.cwd(), 'config.xml');
|
|
1066
|
+
|
|
1067
|
+
try {
|
|
1068
|
+
// 检查是否在 Cordova 项目中
|
|
1069
|
+
if (!fs.existsSync(configXmlPath)) {
|
|
1070
|
+
console.log(chalk.red('Not in a Cordova project directory. Run this command in your project root.'));
|
|
1071
|
+
return;
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
const projectPath = path.resolve(process.cwd(), 'harmonyos');
|
|
1075
|
+
const configXmlPathContent = fs.readFileSync(configXmlPath, 'utf8');
|
|
1076
|
+
const idMatch = configXmlPathContent.match(/id="([^"]+)"/);
|
|
1077
|
+
const packageName = idMatch ? idMatch[1] : "com.example.Myapp";
|
|
1078
|
+
|
|
1079
|
+
const nameMatch = configXmlPathContent.match(/<name>([^<]+)<\/name>/);
|
|
1080
|
+
const projectName = nameMatch ? nameMatch[1] : "MyApp";
|
|
1081
|
+
|
|
1082
|
+
this.platform = platform;
|
|
1083
|
+
this.events = setupEvents(null);
|
|
1084
|
+
this.sourceRoot = path.resolve(__dirname, '..');
|
|
1085
|
+
this.platformProject = new PlatformProject(this);
|
|
1086
|
+
return this.platformProject.create(projectPath, packageName, projectName);
|
|
1087
|
+
} catch (error) {
|
|
1088
|
+
console.log(chalk.red('Failed to add HarmonyOS platform: ' + error.message));
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
showHelp() {
|
|
1093
|
+
console.log(chalk.yellow('hcordova - Cordova CLI with HarmonyOS Support'));
|
|
1094
|
+
console.log('');
|
|
1095
|
+
console.log('Usage:');
|
|
1096
|
+
console.log(' hcordova [command] [options]');
|
|
1097
|
+
console.log('');
|
|
1098
|
+
console.log('Commands:');
|
|
1099
|
+
console.log(' create <path> [id] [name] Create a new Cordova project');
|
|
1100
|
+
console.log(' platform Manage platforms');
|
|
1101
|
+
console.log(' plugin Manage plugins');
|
|
1102
|
+
console.log(' add <platform> Add a platform (shortcut)');
|
|
1103
|
+
console.log(' build <platform> Build project for platform');
|
|
1104
|
+
console.log(' run <platform> Run project on device/emulator');
|
|
1105
|
+
console.log(' list [platforms|plugins] List installed platforms or plugins');
|
|
1106
|
+
console.log(' info Display project information');
|
|
1107
|
+
console.log('');
|
|
1108
|
+
console.log('Options:');
|
|
1109
|
+
console.log(' -v, --version Output version number');
|
|
1110
|
+
console.log(' -h, --help Display help');
|
|
1111
|
+
console.log('');
|
|
1112
|
+
console.log('Examples:');
|
|
1113
|
+
console.log(' hcordova create myapp com.example.myapp MyApp');
|
|
1114
|
+
console.log(' hcordova platform add android');
|
|
1115
|
+
console.log(' hcordova platform add harmonyos');
|
|
1116
|
+
console.log(' hcordova plugin add cordova-plugin-camera');
|
|
1117
|
+
console.log('');
|
|
1118
|
+
console.log(chalk.yellow('Documentation: https://gitcode.net/OpenHarmony-Cordova/hcordova'));
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
showPlatformHelp() {
|
|
1122
|
+
console.log(chalk.yellow('Platform Management:'));
|
|
1123
|
+
console.log('');
|
|
1124
|
+
console.log('Usage:');
|
|
1125
|
+
console.log(' hcordova platform <command>');
|
|
1126
|
+
console.log('');
|
|
1127
|
+
console.log('Commands:');
|
|
1128
|
+
console.log(' add <platform> Add a platform');
|
|
1129
|
+
console.log(' remove <platform> Remove a platform');
|
|
1130
|
+
console.log(' list List installed platforms');
|
|
1131
|
+
console.log('');
|
|
1132
|
+
console.log('Supported platforms:');
|
|
1133
|
+
console.log(' android Android platform (via Cordova)');
|
|
1134
|
+
console.log(' ios iOS platform (via Cordova)');
|
|
1135
|
+
console.log(' harmonyos HarmonyOS platform (custom)');
|
|
1136
|
+
console.log('');
|
|
1137
|
+
console.log('Examples:');
|
|
1138
|
+
console.log(' hcordova platform add android');
|
|
1139
|
+
console.log(' hcordova platform add harmonyos');
|
|
1140
|
+
console.log(' hcordova platform list');
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
showPluginRemoveHelp() {
|
|
1144
|
+
console.log(chalk.yellow('Plugin Removal:'));
|
|
1145
|
+
console.log('');
|
|
1146
|
+
console.log('Usage:');
|
|
1147
|
+
console.log(' hcordova plugin remove <plugin> [options]');
|
|
1148
|
+
console.log('');
|
|
1149
|
+
console.log('Options:');
|
|
1150
|
+
console.log(' --nosave Don\'t remove from config.xml');
|
|
1151
|
+
console.log(' --force Force removal even with errors');
|
|
1152
|
+
console.log('');
|
|
1153
|
+
console.log('Examples:');
|
|
1154
|
+
console.log(' hcordova plugin remove cordova-plugin-camera');
|
|
1155
|
+
console.log(' hcordova plugin remove cordova-plugin-camera --nosave');
|
|
1156
|
+
console.log(' hcordova plugin remove cordova-plugin-camera --force');
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
showPluginHelp() {
|
|
1160
|
+
console.log(chalk.yellow('Plugin Installation:'));
|
|
1161
|
+
console.log('');
|
|
1162
|
+
console.log('Usage:');
|
|
1163
|
+
console.log(' hcordova plugin add <plugin> [options]');
|
|
1164
|
+
console.log('');
|
|
1165
|
+
console.log('Options:');
|
|
1166
|
+
console.log(' --variable NAME=VALUE Set plugin variables');
|
|
1167
|
+
console.log(' --searchpath <path> Search for plugin in specified directory');
|
|
1168
|
+
console.log(' --noregistry Don\'t search in npm registry');
|
|
1169
|
+
console.log(' --link Link the plugin instead of copying');
|
|
1170
|
+
console.log(' --force Force overwrite of existing plugin');
|
|
1171
|
+
console.log(' --nosave Don\'t save to config.xml');
|
|
1172
|
+
console.log('');
|
|
1173
|
+
console.log('Examples:');
|
|
1174
|
+
console.log(' hcordova plugin add cordova-plugin-camera');
|
|
1175
|
+
console.log(' hcordova plugin add cordova-plugin-camera --variable CAMERA_USAGE_DESCRIPTION="需要相机权限"');
|
|
1176
|
+
console.log(' hcordova plugin add ../local/plugin --link');
|
|
1177
|
+
console.log(' hcordova plugin add phonegap-plugin-push@2.1.0');
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
module.exports = BaseCLI;
|