@oniroproject/core 0.8.0 → 0.10.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/dist/index.cjs +375 -244
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +92 -3
- package/dist/index.d.ts +92 -3
- package/dist/index.js +369 -245
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -47,6 +47,7 @@ __export(index_exports, {
|
|
|
47
47
|
VERSION: () => VERSION,
|
|
48
48
|
applyChanges: () => applyChanges,
|
|
49
49
|
attemptHdcConnection: () => attemptHdcConnection,
|
|
50
|
+
buildCmdWrapper: () => buildCmdWrapper,
|
|
50
51
|
buildGestureCommands: () => buildGestureCommands,
|
|
51
52
|
buildGestureUitestCommand: () => buildGestureUitestCommand,
|
|
52
53
|
buildHap: () => buildHap,
|
|
@@ -83,6 +84,8 @@ __export(index_exports, {
|
|
|
83
84
|
ensureOhModules: () => ensureOhModules,
|
|
84
85
|
ensureOk: () => ensureOk,
|
|
85
86
|
ensureRemoteOk: () => ensureRemoteOk,
|
|
87
|
+
escapeCmdArgument: () => escapeCmdArgument,
|
|
88
|
+
escapeCmdCommand: () => escapeCmdCommand,
|
|
86
89
|
extractTarball: () => extractTarball,
|
|
87
90
|
extractZipWithProgress: () => extractZipWithProgress,
|
|
88
91
|
findAppProcessId: () => findAppProcessId,
|
|
@@ -121,12 +124,14 @@ __export(index_exports, {
|
|
|
121
124
|
isRamBackedPath: () => isRamBackedPath,
|
|
122
125
|
isValidBundleName: () => isValidBundleName,
|
|
123
126
|
isValidProjectName: () => isValidProjectName,
|
|
127
|
+
killProcessTree: () => killProcessTree,
|
|
124
128
|
launchApp: () => launchApp,
|
|
125
129
|
listAbilities: () => listAbilities,
|
|
126
130
|
listDevices: () => listDevices,
|
|
127
131
|
listModules: () => listModules,
|
|
128
132
|
listRunningProcesses: () => listRunningProcesses,
|
|
129
133
|
listTemplates: () => listTemplates,
|
|
134
|
+
needsCmdWrapper: () => needsCmdWrapper,
|
|
130
135
|
nonInteractivePrompter: () => nonInteractivePrompter,
|
|
131
136
|
noopLogger: () => noopLogger,
|
|
132
137
|
noopProgress: () => noopProgress,
|
|
@@ -163,11 +168,13 @@ __export(index_exports, {
|
|
|
163
168
|
setHilogLevel: () => setHilogLevel,
|
|
164
169
|
shell: () => shell,
|
|
165
170
|
shellChecked: () => shellChecked,
|
|
171
|
+
spawnCompat: () => spawnCompat,
|
|
166
172
|
startEmulator: () => startEmulator,
|
|
167
173
|
staticConfig: () => staticConfig,
|
|
168
174
|
stopEmulator: () => stopEmulator,
|
|
169
175
|
streamHilog: () => streamHilog,
|
|
170
176
|
takeScreenshot: () => takeScreenshot,
|
|
177
|
+
toLongPath: () => toLongPath,
|
|
171
178
|
toSpaceError: () => toSpaceError,
|
|
172
179
|
uninstallApp: () => uninstallApp,
|
|
173
180
|
validateTemplateLayout: () => validateTemplateLayout,
|
|
@@ -402,6 +409,14 @@ function getCmdToolsPath(config) {
|
|
|
402
409
|
function getEmulatorDir(config) {
|
|
403
410
|
return config.get("emulatorDir", defaultPaths.emulatorDir());
|
|
404
411
|
}
|
|
412
|
+
function toLongPath(p, platform7 = os3.platform()) {
|
|
413
|
+
if (platform7 !== "win32") return p;
|
|
414
|
+
try {
|
|
415
|
+
return fs.realpathSync.native(p);
|
|
416
|
+
} catch {
|
|
417
|
+
return p;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
405
420
|
function pickExisting(candidates) {
|
|
406
421
|
for (const c of candidates) {
|
|
407
422
|
if (fs.existsSync(c)) return c;
|
|
@@ -437,12 +452,11 @@ function getHdcPath(config) {
|
|
|
437
452
|
}
|
|
438
453
|
return path2.join(base, "hdc");
|
|
439
454
|
}
|
|
440
|
-
function getHvigorwPath(config, projectDir) {
|
|
441
|
-
const
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
]);
|
|
455
|
+
function getHvigorwPath(config, projectDir, platform7 = os3.platform()) {
|
|
456
|
+
const posixFirst = ["hvigorw", "hvigorw.bat", "hvigorw.cmd"];
|
|
457
|
+
const windowsFirst = ["hvigorw.bat", "hvigorw.cmd", "hvigorw"];
|
|
458
|
+
const names = platform7 === "win32" ? windowsFirst : posixFirst;
|
|
459
|
+
const local = pickExisting(names.map((n) => path2.join(projectDir, n)));
|
|
446
460
|
if (fs.existsSync(local) && isProjectLocalHvigorwWorking(projectDir)) {
|
|
447
461
|
return local;
|
|
448
462
|
}
|
|
@@ -522,7 +536,7 @@ function createInstallTempDir(opts) {
|
|
|
522
536
|
} catch (err) {
|
|
523
537
|
const fallback = os4.tmpdir();
|
|
524
538
|
if (requested || isOutOfSpaceError(err) || path4.resolve(fallback) === path4.resolve(root)) throw err;
|
|
525
|
-
const reason = err instanceof Error ? err.message.split(
|
|
539
|
+
const reason = err instanceof Error ? err.message.split(/\r?\n/)[0] : String(err);
|
|
526
540
|
(opts.logger ?? noopLogger).warn(`${reason} Falling back to '${fallback}'.`);
|
|
527
541
|
return { dir: createTempWorkDir(fallback, opts.prefix), root: fallback };
|
|
528
542
|
}
|
|
@@ -974,8 +988,82 @@ async function downloadAndInstallSdk(opts) {
|
|
|
974
988
|
// src/cmdTools/install.ts
|
|
975
989
|
var fs10 = __toESM(require("fs"), 1);
|
|
976
990
|
var os6 = __toESM(require("os"), 1);
|
|
991
|
+
var path11 = __toESM(require("path"), 1);
|
|
992
|
+
var import_node_child_process2 = require("child_process");
|
|
993
|
+
|
|
994
|
+
// src/hdc/spawnCompat.ts
|
|
977
995
|
var path10 = __toESM(require("path"), 1);
|
|
978
996
|
var import_node_child_process = require("child_process");
|
|
997
|
+
var WINDOWS_BATCH_RE = /\.(bat|cmd)$/i;
|
|
998
|
+
var CMD_META_RE = /([()[\]%!^"`<>&|;, *?])/g;
|
|
999
|
+
var CMD_SHIM_RE = /node_modules[\\/]\.bin[\\/][^\\/]+\.cmd$/i;
|
|
1000
|
+
var UNSAFE_CMD_CHARS_RE = /["\r\n\0]/;
|
|
1001
|
+
function needsCmdWrapper(command, platform7 = process.platform) {
|
|
1002
|
+
return platform7 === "win32" && WINDOWS_BATCH_RE.test(command);
|
|
1003
|
+
}
|
|
1004
|
+
function escapeCmdArgument(arg, doubleEscapeMetaChars = false) {
|
|
1005
|
+
let out = String(arg);
|
|
1006
|
+
out = out.replace(/(\\*)"/g, '$1$1\\"');
|
|
1007
|
+
out = out.replace(/(\\*)$/, "$1$1");
|
|
1008
|
+
out = `"${out}"`;
|
|
1009
|
+
out = out.replace(CMD_META_RE, "^$1");
|
|
1010
|
+
if (doubleEscapeMetaChars) out = out.replace(CMD_META_RE, "^$1");
|
|
1011
|
+
return out;
|
|
1012
|
+
}
|
|
1013
|
+
function escapeCmdCommand(command) {
|
|
1014
|
+
return command.replace(CMD_META_RE, "^$1");
|
|
1015
|
+
}
|
|
1016
|
+
function buildCmdWrapper(command, args) {
|
|
1017
|
+
assertCmdSafe(command, `command ${command}`);
|
|
1018
|
+
args.forEach((a, i) => assertCmdSafe(a, `argument ${i} (${a})`));
|
|
1019
|
+
const normalized = path10.normalize(command);
|
|
1020
|
+
const double = CMD_SHIM_RE.test(normalized);
|
|
1021
|
+
const line = [escapeCmdCommand(normalized), ...args.map((a) => escapeCmdArgument(a, double))].join(" ");
|
|
1022
|
+
return {
|
|
1023
|
+
command: process.env.ComSpec ?? process.env.COMSPEC ?? "cmd.exe",
|
|
1024
|
+
args: ["/d", "/s", "/c", `"${line}"`]
|
|
1025
|
+
};
|
|
1026
|
+
}
|
|
1027
|
+
function assertCmdSafe(value, label) {
|
|
1028
|
+
if (UNSAFE_CMD_CHARS_RE.test(value)) {
|
|
1029
|
+
throw new OniroError(
|
|
1030
|
+
`Cannot run a Windows batch wrapper with a quote, newline or NUL in the ${label}: cmd.exe cannot pass it through unambiguously.`
|
|
1031
|
+
);
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
function spawnCompat(command, args, options = {}) {
|
|
1035
|
+
if (!needsCmdWrapper(command)) {
|
|
1036
|
+
return (0, import_node_child_process.spawn)(command, [...args], { ...options, shell: false });
|
|
1037
|
+
}
|
|
1038
|
+
const wrapped = buildCmdWrapper(command, args);
|
|
1039
|
+
return (0, import_node_child_process.spawn)(wrapped.command, wrapped.args, {
|
|
1040
|
+
...options,
|
|
1041
|
+
shell: false,
|
|
1042
|
+
// The line is already escaped; stop Node from re-quoting it.
|
|
1043
|
+
windowsVerbatimArguments: true
|
|
1044
|
+
});
|
|
1045
|
+
}
|
|
1046
|
+
function killProcessTree(child) {
|
|
1047
|
+
if (process.platform === "win32" && typeof child.pid === "number") {
|
|
1048
|
+
try {
|
|
1049
|
+
const killer = (0, import_node_child_process.spawn)("taskkill", ["/pid", String(child.pid), "/T", "/F"], { stdio: "ignore" });
|
|
1050
|
+
killer.on("error", () => {
|
|
1051
|
+
try {
|
|
1052
|
+
child.kill("SIGKILL");
|
|
1053
|
+
} catch {
|
|
1054
|
+
}
|
|
1055
|
+
});
|
|
1056
|
+
return;
|
|
1057
|
+
} catch {
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
try {
|
|
1061
|
+
child.kill("SIGKILL");
|
|
1062
|
+
} catch {
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
// src/cmdTools/install.ts
|
|
979
1067
|
function getCmdToolsDownloadUrl(config, platform7 = os6.platform()) {
|
|
980
1068
|
if (platform7 === "linux") {
|
|
981
1069
|
return config.get("cmdToolsUrlLinux", defaultPaths.cmdToolsUrlLinux);
|
|
@@ -1002,16 +1090,16 @@ function getCmdToolsDownloadUrl(config, platform7 = os6.platform()) {
|
|
|
1002
1090
|
}
|
|
1003
1091
|
function findCmdToolsSourceDir(extractPath) {
|
|
1004
1092
|
const known = [
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1093
|
+
path11.join(extractPath, "command-line-tools"),
|
|
1094
|
+
path11.join(extractPath, "oh-command-line-tools"),
|
|
1095
|
+
path11.join(extractPath, "commandline-tools")
|
|
1008
1096
|
];
|
|
1009
1097
|
for (const c of known) {
|
|
1010
1098
|
if (fs10.existsSync(c)) return c;
|
|
1011
1099
|
}
|
|
1012
|
-
const entries = fs10.readdirSync(extractPath, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) =>
|
|
1100
|
+
const entries = fs10.readdirSync(extractPath, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => path11.join(extractPath, e.name));
|
|
1013
1101
|
for (const dir of entries) {
|
|
1014
|
-
if (fs10.existsSync(
|
|
1102
|
+
if (fs10.existsSync(path11.join(dir, "bin"))) return dir;
|
|
1015
1103
|
}
|
|
1016
1104
|
throw new OniroError("Could not locate command line tools folder in the extracted archive.");
|
|
1017
1105
|
}
|
|
@@ -1023,12 +1111,12 @@ async function installCmdTools(opts) {
|
|
|
1023
1111
|
const { dir: tmpDir, root: tmpRoot } = createInstallTempDir({
|
|
1024
1112
|
config,
|
|
1025
1113
|
override: opts.tmpDir,
|
|
1026
|
-
installRoot:
|
|
1114
|
+
installRoot: path11.dirname(CMD_PATH),
|
|
1027
1115
|
prefix: "oniro-cmdtools-",
|
|
1028
1116
|
logger
|
|
1029
1117
|
});
|
|
1030
|
-
const zipPath = opts.localZipPath ??
|
|
1031
|
-
const extractPath =
|
|
1118
|
+
const zipPath = opts.localZipPath ?? path11.join(tmpDir, "oh-command-line-tools.zip");
|
|
1119
|
+
const extractPath = path11.join(tmpDir, "oh-command-line-tools");
|
|
1032
1120
|
try {
|
|
1033
1121
|
if (!opts.localZipPath) {
|
|
1034
1122
|
const url = getCmdToolsDownloadUrl(config);
|
|
@@ -1040,8 +1128,8 @@ async function installCmdTools(opts) {
|
|
|
1040
1128
|
fs10.mkdirSync(CMD_PATH, { recursive: true });
|
|
1041
1129
|
const srcDir = findCmdToolsSourceDir(extractPath);
|
|
1042
1130
|
for (const entry of fs10.readdirSync(srcDir)) {
|
|
1043
|
-
const src =
|
|
1044
|
-
const dest =
|
|
1131
|
+
const src = path11.join(srcDir, entry);
|
|
1132
|
+
const dest = path11.join(CMD_PATH, entry);
|
|
1045
1133
|
if (fs10.statSync(src).isDirectory()) {
|
|
1046
1134
|
if (fs10.existsSync(dest)) fs10.rmSync(dest, { recursive: true, force: true });
|
|
1047
1135
|
movePath(src, dest);
|
|
@@ -1049,10 +1137,10 @@ async function installCmdTools(opts) {
|
|
|
1049
1137
|
fs10.copyFileSync(src, dest);
|
|
1050
1138
|
}
|
|
1051
1139
|
}
|
|
1052
|
-
const binDir =
|
|
1140
|
+
const binDir = path11.join(CMD_PATH, "bin");
|
|
1053
1141
|
if (fs10.existsSync(binDir) && os6.platform() !== "win32") {
|
|
1054
1142
|
for (const file of fs10.readdirSync(binDir)) {
|
|
1055
|
-
fs10.chmodSync(
|
|
1143
|
+
fs10.chmodSync(path11.join(binDir, file), 493);
|
|
1056
1144
|
}
|
|
1057
1145
|
}
|
|
1058
1146
|
progress?.report({ message: "Finalizing installation...", increment: 5 });
|
|
@@ -1065,14 +1153,14 @@ async function installCmdTools(opts) {
|
|
|
1065
1153
|
}
|
|
1066
1154
|
function getCmdToolsStatus(config) {
|
|
1067
1155
|
const cmdPath = getCmdToolsPath(config);
|
|
1068
|
-
const binDir =
|
|
1156
|
+
const binDir = path11.join(cmdPath, "bin");
|
|
1069
1157
|
const candidates = os6.platform() === "win32" ? ["ohpm.exe", "ohpm.cmd", "ohpm.bat", "ohpm"] : ["ohpm"];
|
|
1070
|
-
const bin = candidates.map((c) =>
|
|
1158
|
+
const bin = candidates.map((c) => path11.join(binDir, c)).find((p) => fs10.existsSync(p));
|
|
1071
1159
|
if (!bin) {
|
|
1072
1160
|
return { installed: false, status: "Not installed" };
|
|
1073
1161
|
}
|
|
1074
1162
|
try {
|
|
1075
|
-
const versionFile =
|
|
1163
|
+
const versionFile = path11.join(cmdPath, "version.txt");
|
|
1076
1164
|
if (fs10.existsSync(versionFile)) {
|
|
1077
1165
|
const content = fs10.readFileSync(versionFile, "utf8");
|
|
1078
1166
|
for (const line of content.split(/\r?\n/)) {
|
|
@@ -1083,7 +1171,14 @@ function getCmdToolsStatus(config) {
|
|
|
1083
1171
|
} catch {
|
|
1084
1172
|
}
|
|
1085
1173
|
try {
|
|
1086
|
-
const
|
|
1174
|
+
const wrapped = needsCmdWrapper(bin);
|
|
1175
|
+
const probe = wrapped ? buildCmdWrapper(bin, ["-v"]) : { command: bin, args: ["-v"] };
|
|
1176
|
+
const res = (0, import_node_child_process2.spawnSync)(probe.command, probe.args, {
|
|
1177
|
+
encoding: "utf8",
|
|
1178
|
+
windowsVerbatimArguments: wrapped
|
|
1179
|
+
});
|
|
1180
|
+
if (res.error || res.status !== 0) throw res.error ?? new Error(`exit ${String(res.status)}`);
|
|
1181
|
+
const version = res.stdout.trim();
|
|
1087
1182
|
return { installed: true, status: `Installed (${version})` };
|
|
1088
1183
|
} catch {
|
|
1089
1184
|
return { installed: true, status: "Installed (version unknown)" };
|
|
@@ -1101,7 +1196,7 @@ function removeCmdTools(config) {
|
|
|
1101
1196
|
|
|
1102
1197
|
// src/sign/encryptKey.ts
|
|
1103
1198
|
var fs11 = __toESM(require("fs"), 1);
|
|
1104
|
-
var
|
|
1199
|
+
var path12 = __toESM(require("path"), 1);
|
|
1105
1200
|
var crypto2 = __toESM(require("crypto"), 1);
|
|
1106
1201
|
var DAEMON_ROOT_KEY_COMPONENT_LENGTH = 16;
|
|
1107
1202
|
var DAEMON_SALT_KEY_LENGTH = 16;
|
|
@@ -1173,7 +1268,7 @@ function getRootKey(fdComponents, salt) {
|
|
|
1173
1268
|
function createAndStoreKey(dir, keyLength) {
|
|
1174
1269
|
const key = crypto2.randomBytes(keyLength);
|
|
1175
1270
|
const hash = crypto2.createHash("sha256").update(key).digest("hex");
|
|
1176
|
-
const filePath =
|
|
1271
|
+
const filePath = path12.join(dir, hash);
|
|
1177
1272
|
fs11.writeFileSync(filePath, key);
|
|
1178
1273
|
fs11.chmodSync(filePath, KEY_FILE_PERMISSIONS);
|
|
1179
1274
|
return key;
|
|
@@ -1182,23 +1277,23 @@ function createAndStoreEnKey(rootKey, ceDir) {
|
|
|
1182
1277
|
const workKey = crypto2.randomBytes(DAEMON_WORK_KEY_LENGTH);
|
|
1183
1278
|
const encrypted = encrypt(rootKey, workKey);
|
|
1184
1279
|
const hash = crypto2.createHash("sha256").update(encrypted).digest("hex");
|
|
1185
|
-
const filePath =
|
|
1280
|
+
const filePath = path12.join(ceDir, hash);
|
|
1186
1281
|
fs11.writeFileSync(filePath, encrypted);
|
|
1187
1282
|
fs11.chmodSync(filePath, KEY_FILE_PERMISSIONS);
|
|
1188
1283
|
}
|
|
1189
1284
|
function createMaterial(materialPath) {
|
|
1190
1285
|
fs11.mkdirSync(materialPath, { recursive: true });
|
|
1191
1286
|
fs11.chmodSync(materialPath, KEY_FILE_DIRECTORY_PERMISSIONS);
|
|
1192
|
-
const fdDir =
|
|
1193
|
-
const acDir =
|
|
1194
|
-
const ceDir =
|
|
1287
|
+
const fdDir = path12.join(materialPath, "fd");
|
|
1288
|
+
const acDir = path12.join(materialPath, "ac");
|
|
1289
|
+
const ceDir = path12.join(materialPath, "ce");
|
|
1195
1290
|
fs11.mkdirSync(fdDir, { recursive: true });
|
|
1196
1291
|
fs11.mkdirSync(acDir, { recursive: true });
|
|
1197
1292
|
fs11.mkdirSync(ceDir, { recursive: true });
|
|
1198
1293
|
const fdSubDirs = ["0", "1", "2"];
|
|
1199
1294
|
const fdComponents = [];
|
|
1200
1295
|
for (const sub of fdSubDirs) {
|
|
1201
|
-
const subDir =
|
|
1296
|
+
const subDir = path12.join(fdDir, sub);
|
|
1202
1297
|
fs11.mkdirSync(subDir, { recursive: true });
|
|
1203
1298
|
const comp = createAndStoreKey(subDir, DAEMON_ROOT_KEY_COMPONENT_LENGTH);
|
|
1204
1299
|
fdComponents.push(comp);
|
|
@@ -1215,17 +1310,17 @@ function readSingleFile(dir) {
|
|
|
1215
1310
|
if (files.length !== 1) {
|
|
1216
1311
|
throw new Error(`Signing material in ${dir} is illegal (expected exactly one file).`);
|
|
1217
1312
|
}
|
|
1218
|
-
return fs11.readFileSync(
|
|
1313
|
+
return fs11.readFileSync(path12.join(dir, files[0]));
|
|
1219
1314
|
}
|
|
1220
1315
|
function getKey(materialPath) {
|
|
1221
1316
|
if (!fs11.existsSync(materialPath) || !fs11.statSync(materialPath).isDirectory()) {
|
|
1222
1317
|
throw new Error("Material directory does not exist.");
|
|
1223
1318
|
}
|
|
1224
|
-
const fdDir =
|
|
1225
|
-
const fdComponents = ["0", "1", "2"].map((sub) => readSingleFile(
|
|
1226
|
-
const salt = readSingleFile(
|
|
1319
|
+
const fdDir = path12.join(materialPath, "fd");
|
|
1320
|
+
const fdComponents = ["0", "1", "2"].map((sub) => readSingleFile(path12.join(fdDir, sub)));
|
|
1321
|
+
const salt = readSingleFile(path12.join(materialPath, "ac"));
|
|
1227
1322
|
const rootKey = getRootKey(fdComponents, salt);
|
|
1228
|
-
const workMaterial = readSingleFile(
|
|
1323
|
+
const workMaterial = readSingleFile(path12.join(materialPath, "ce"));
|
|
1229
1324
|
return decrypt(rootKey, workMaterial);
|
|
1230
1325
|
}
|
|
1231
1326
|
function encryptPwd(password, materialPath) {
|
|
@@ -1241,8 +1336,8 @@ function decryptPwd(encryptedHex, materialPath) {
|
|
|
1241
1336
|
|
|
1242
1337
|
// src/sign/generateSigningConfigs.ts
|
|
1243
1338
|
var fs12 = __toESM(require("fs"), 1);
|
|
1244
|
-
var
|
|
1245
|
-
var
|
|
1339
|
+
var path13 = __toESM(require("path"), 1);
|
|
1340
|
+
var import_node_child_process3 = require("child_process");
|
|
1246
1341
|
var import_json52 = __toESM(require("json5"), 1);
|
|
1247
1342
|
|
|
1248
1343
|
// src/sign/applicationCert.ts
|
|
@@ -1311,13 +1406,13 @@ var SIGNING_KIND_CONFIG = {
|
|
|
1311
1406
|
};
|
|
1312
1407
|
function copyFilesToProject(projectDir, paths, kind, logger, applicationCertPath) {
|
|
1313
1408
|
logger.info("[sign] Copying signing material into project...");
|
|
1314
|
-
const signaturesDir =
|
|
1409
|
+
const signaturesDir = path13.join(projectDir, "signatures");
|
|
1315
1410
|
fs12.mkdirSync(signaturesDir, { recursive: true });
|
|
1316
|
-
fs12.copyFileSync(paths.keystore,
|
|
1317
|
-
fs12.copyFileSync(paths.profileCert,
|
|
1318
|
-
fs12.copyFileSync(paths.unsignedProfileTemplate,
|
|
1411
|
+
fs12.copyFileSync(paths.keystore, path13.join(signaturesDir, "OpenHarmony.p12"));
|
|
1412
|
+
fs12.copyFileSync(paths.profileCert, path13.join(signaturesDir, "OpenHarmonyProfileRelease.pem"));
|
|
1413
|
+
fs12.copyFileSync(paths.unsignedProfileTemplate, path13.join(signaturesDir, "UnsgnedReleasedProfileTemplate.json"));
|
|
1319
1414
|
if (kind === "application-release") {
|
|
1320
|
-
const dest =
|
|
1415
|
+
const dest = path13.join(signaturesDir, "OpenHarmonyApplication.cer");
|
|
1321
1416
|
if (applicationCertPath) {
|
|
1322
1417
|
fs12.copyFileSync(applicationCertPath, dest);
|
|
1323
1418
|
} else {
|
|
@@ -1340,8 +1435,8 @@ function extractLeafCertificate(chainPath) {
|
|
|
1340
1435
|
function modifyProfileTemplate(projectDir, apl, appFeature, logger, options = {}) {
|
|
1341
1436
|
const kind = options.kind ?? pickSigningKind(apl);
|
|
1342
1437
|
logger.info(`[sign] Modifying profile template (apl=${apl}, app-feature=${appFeature}, kind=${kind})...`);
|
|
1343
|
-
const appJsonPath =
|
|
1344
|
-
const profileTemplatePath =
|
|
1438
|
+
const appJsonPath = path13.join(projectDir, "AppScope/app.json5");
|
|
1439
|
+
const profileTemplatePath = path13.join(projectDir, "signatures/UnsgnedReleasedProfileTemplate.json");
|
|
1345
1440
|
if (!fs12.existsSync(appJsonPath)) {
|
|
1346
1441
|
throw new OniroError(`${appJsonPath} does not exist.`);
|
|
1347
1442
|
}
|
|
@@ -1366,7 +1461,7 @@ function modifyProfileTemplate(projectDir, apl, appFeature, logger, options = {}
|
|
|
1366
1461
|
profileTemplate["bundle-info"]["bundle-name"] = appJson.app.bundleName;
|
|
1367
1462
|
profileTemplate["bundle-info"]["apl"] = apl;
|
|
1368
1463
|
profileTemplate["bundle-info"]["app-feature"] = appFeature;
|
|
1369
|
-
const distCertChainPath =
|
|
1464
|
+
const distCertChainPath = path13.join(
|
|
1370
1465
|
projectDir,
|
|
1371
1466
|
"signatures",
|
|
1372
1467
|
SIGNING_KIND_CONFIG[kind].certBasename
|
|
@@ -1380,9 +1475,9 @@ function modifyProfileTemplate(projectDir, apl, appFeature, logger, options = {}
|
|
|
1380
1475
|
}
|
|
1381
1476
|
function generateP7bFile(projectDir, paths, logger, passwords) {
|
|
1382
1477
|
logger.info("[sign] Generating P7b profile via hap-sign-tool...");
|
|
1383
|
-
const signaturesDir =
|
|
1384
|
-
const profileTemplatePath =
|
|
1385
|
-
const outputProfilePath =
|
|
1478
|
+
const signaturesDir = path13.join(projectDir, "signatures");
|
|
1479
|
+
const profileTemplatePath = path13.join(signaturesDir, "UnsgnedReleasedProfileTemplate.json");
|
|
1480
|
+
const outputProfilePath = path13.join(signaturesDir, "app1-profile.p7b");
|
|
1386
1481
|
const args = [
|
|
1387
1482
|
"-jar",
|
|
1388
1483
|
paths.signTool,
|
|
@@ -1407,7 +1502,7 @@ function generateP7bFile(projectDir, paths, logger, passwords) {
|
|
|
1407
1502
|
passwords.store
|
|
1408
1503
|
];
|
|
1409
1504
|
try {
|
|
1410
|
-
(0,
|
|
1505
|
+
(0, import_node_child_process3.execFileSync)("java", args, { stdio: "inherit" });
|
|
1411
1506
|
} catch (e) {
|
|
1412
1507
|
throw new OniroError(
|
|
1413
1508
|
`hap-sign-tool failed. Ensure a JDK (with keytool/java) is on PATH. Underlying error: ${e.message}`,
|
|
@@ -1416,7 +1511,7 @@ function generateP7bFile(projectDir, paths, logger, passwords) {
|
|
|
1416
1511
|
}
|
|
1417
1512
|
}
|
|
1418
1513
|
function detectSigningConfigNames(projectDir) {
|
|
1419
|
-
const buildProfilePath =
|
|
1514
|
+
const buildProfilePath = path13.join(projectDir, "build-profile.json5");
|
|
1420
1515
|
if (!fs12.existsSync(buildProfilePath)) return ["default"];
|
|
1421
1516
|
let parsed;
|
|
1422
1517
|
try {
|
|
@@ -1437,8 +1532,8 @@ function detectSigningConfigNames(projectDir) {
|
|
|
1437
1532
|
}
|
|
1438
1533
|
function updateBuildProfile(projectDir, kind, logger, passwords) {
|
|
1439
1534
|
logger.info("[sign] Writing signing configs into build-profile.json5...");
|
|
1440
|
-
const materialDir =
|
|
1441
|
-
const buildProfilePath =
|
|
1535
|
+
const materialDir = path13.join(projectDir, "signatures", "material");
|
|
1536
|
+
const buildProfilePath = path13.join(projectDir, "build-profile.json5");
|
|
1442
1537
|
const encryptedStorePassword = encryptPwd(passwords.store, materialDir);
|
|
1443
1538
|
const encryptedKeyPassword = encryptPwd(passwords.key, materialDir);
|
|
1444
1539
|
let buildProfile = { app: {} };
|
|
@@ -1469,7 +1564,7 @@ function updateBuildProfile(projectDir, kind, logger, passwords) {
|
|
|
1469
1564
|
}
|
|
1470
1565
|
function prepareMaterialDirectory(projectDir, logger) {
|
|
1471
1566
|
logger.info("[sign] Generating fresh signing material...");
|
|
1472
|
-
const materialDir =
|
|
1567
|
+
const materialDir = path13.join(projectDir, "signatures", "material");
|
|
1473
1568
|
if (fs12.existsSync(materialDir)) {
|
|
1474
1569
|
fs12.rmSync(materialDir, { recursive: true, force: true });
|
|
1475
1570
|
}
|
|
@@ -1494,15 +1589,15 @@ function generateSigningConfigs(options) {
|
|
|
1494
1589
|
"Could not detect project SDK version (compileSdkVersion missing in build-profile.json5)."
|
|
1495
1590
|
);
|
|
1496
1591
|
}
|
|
1497
|
-
const sdkPath =
|
|
1592
|
+
const sdkPath = path13.join(sdkHome, String(sdkVersion));
|
|
1498
1593
|
if (!fs12.existsSync(sdkPath)) {
|
|
1499
1594
|
throw new OniroError(`SDK path does not exist: ${sdkPath}`);
|
|
1500
1595
|
}
|
|
1501
1596
|
const paths = {
|
|
1502
|
-
signTool:
|
|
1503
|
-
keystore:
|
|
1504
|
-
profileCert:
|
|
1505
|
-
unsignedProfileTemplate:
|
|
1597
|
+
signTool: path13.join(sdkPath, "toolchains/lib/hap-sign-tool.jar"),
|
|
1598
|
+
keystore: path13.join(sdkPath, "toolchains/lib/OpenHarmony.p12"),
|
|
1599
|
+
profileCert: path13.join(sdkPath, "toolchains/lib/OpenHarmonyProfileRelease.pem"),
|
|
1600
|
+
unsignedProfileTemplate: path13.join(sdkPath, "toolchains/lib/UnsgnedReleasedProfileTemplate.json")
|
|
1506
1601
|
};
|
|
1507
1602
|
const kind = pickSigningKind(apl);
|
|
1508
1603
|
const passwords = options.passwords ?? DEFAULT_PASSWORDS;
|
|
@@ -1517,7 +1612,7 @@ function generateSigningConfigs(options) {
|
|
|
1517
1612
|
|
|
1518
1613
|
// src/emulator/install.ts
|
|
1519
1614
|
var fs13 = __toESM(require("fs"), 1);
|
|
1520
|
-
var
|
|
1615
|
+
var path14 = __toESM(require("path"), 1);
|
|
1521
1616
|
async function installEmulator(opts) {
|
|
1522
1617
|
const { config, progress, abortSignal } = opts;
|
|
1523
1618
|
const logger = opts.logger ?? noopLogger;
|
|
@@ -1527,18 +1622,18 @@ async function installEmulator(opts) {
|
|
|
1527
1622
|
const { dir: tmpDir, root: tmpRoot } = createInstallTempDir({
|
|
1528
1623
|
config,
|
|
1529
1624
|
override: opts.tmpDir,
|
|
1530
|
-
installRoot:
|
|
1625
|
+
installRoot: path14.dirname(emulatorDir),
|
|
1531
1626
|
prefix: "oniro-emulator-",
|
|
1532
1627
|
logger
|
|
1533
1628
|
});
|
|
1534
|
-
const tmpZip =
|
|
1629
|
+
const tmpZip = path14.join(tmpDir, "oniro_emulator.zip");
|
|
1535
1630
|
try {
|
|
1536
1631
|
fs13.mkdirSync(emulatorDir, { recursive: true });
|
|
1537
1632
|
progress?.report({ message: "Downloading emulator...", increment: 0 });
|
|
1538
1633
|
await downloadFile({ url, dest: tmpZip, progress, abortSignal, start: 0, range: 50, what, tmpRoot });
|
|
1539
1634
|
progress?.report({ message: "Extracting emulator...", increment: 0 });
|
|
1540
1635
|
await extractZipWithProgress({ zipPath: tmpZip, dest: emulatorDir, progress, start: 50, range: 45, logger });
|
|
1541
|
-
const runSh =
|
|
1636
|
+
const runSh = path14.join(emulatorDir, "images", "run.sh");
|
|
1542
1637
|
if (fs13.existsSync(runSh)) {
|
|
1543
1638
|
fs13.chmodSync(runSh, 493);
|
|
1544
1639
|
}
|
|
@@ -1550,8 +1645,8 @@ async function installEmulator(opts) {
|
|
|
1550
1645
|
}
|
|
1551
1646
|
}
|
|
1552
1647
|
function isEmulatorInstalled(config) {
|
|
1553
|
-
const imagesDir =
|
|
1554
|
-
return fs13.existsSync(
|
|
1648
|
+
const imagesDir = path14.join(getEmulatorDir(config), "images");
|
|
1649
|
+
return fs13.existsSync(path14.join(imagesDir, "run.sh")) || fs13.existsSync(path14.join(imagesDir, "run.bat"));
|
|
1555
1650
|
}
|
|
1556
1651
|
function removeEmulator(config) {
|
|
1557
1652
|
const emulatorDir = getEmulatorDir(config);
|
|
@@ -1561,13 +1656,16 @@ function removeEmulator(config) {
|
|
|
1561
1656
|
}
|
|
1562
1657
|
|
|
1563
1658
|
// src/emulator/lifecycle.ts
|
|
1564
|
-
var
|
|
1659
|
+
var fs15 = __toESM(require("fs"), 1);
|
|
1565
1660
|
var os7 = __toESM(require("os"), 1);
|
|
1566
|
-
var
|
|
1661
|
+
var path15 = __toESM(require("path"), 1);
|
|
1567
1662
|
var import_node_child_process4 = require("child_process");
|
|
1568
1663
|
|
|
1569
1664
|
// src/hdc/exec.ts
|
|
1570
|
-
var
|
|
1665
|
+
var fs14 = __toESM(require("fs"), 1);
|
|
1666
|
+
function errorMessage(err) {
|
|
1667
|
+
return err instanceof Error ? err.message : String(err);
|
|
1668
|
+
}
|
|
1571
1669
|
function runProcess(opts) {
|
|
1572
1670
|
const { command, args, cwd, env, onOutput } = opts;
|
|
1573
1671
|
const timeoutMs = opts.timeoutMs ?? 6e4;
|
|
@@ -1577,7 +1675,17 @@ function runProcess(opts) {
|
|
|
1577
1675
|
reject(new CancelledError(`Command aborted before start: ${command}`));
|
|
1578
1676
|
return;
|
|
1579
1677
|
}
|
|
1580
|
-
|
|
1678
|
+
if (needsCmdWrapper(command) && !fs14.existsSync(command)) {
|
|
1679
|
+
reject(new OniroError(`Failed to spawn ${command}: ENOENT (no such file)`));
|
|
1680
|
+
return;
|
|
1681
|
+
}
|
|
1682
|
+
let child;
|
|
1683
|
+
try {
|
|
1684
|
+
child = spawnCompat(command, args, { cwd, env });
|
|
1685
|
+
} catch (err) {
|
|
1686
|
+
reject(new OniroError(`Failed to spawn ${command}: ${errorMessage(err)}`, err));
|
|
1687
|
+
return;
|
|
1688
|
+
}
|
|
1581
1689
|
let stdout = "";
|
|
1582
1690
|
let stderr = "";
|
|
1583
1691
|
let settled = false;
|
|
@@ -1590,26 +1698,20 @@ function runProcess(opts) {
|
|
|
1590
1698
|
if (settled) return;
|
|
1591
1699
|
settled = true;
|
|
1592
1700
|
cleanup();
|
|
1593
|
-
|
|
1594
|
-
child.kill("SIGKILL");
|
|
1595
|
-
} catch {
|
|
1596
|
-
}
|
|
1701
|
+
killProcessTree(child);
|
|
1597
1702
|
reject(new CancelledError(`Command aborted: ${command} ${args.join(" ")}`));
|
|
1598
1703
|
};
|
|
1599
1704
|
const timer = setTimeout(() => {
|
|
1600
1705
|
timedOut = true;
|
|
1601
|
-
|
|
1602
|
-
child.kill("SIGKILL");
|
|
1603
|
-
} catch {
|
|
1604
|
-
}
|
|
1706
|
+
killProcessTree(child);
|
|
1605
1707
|
}, timeoutMs);
|
|
1606
1708
|
abortSignal?.addEventListener("abort", onAbort, { once: true });
|
|
1607
|
-
child.stdout
|
|
1709
|
+
child.stdout?.on("data", (d) => {
|
|
1608
1710
|
const s = d.toString();
|
|
1609
1711
|
stdout += s;
|
|
1610
1712
|
onOutput?.(s, "stdout");
|
|
1611
1713
|
});
|
|
1612
|
-
child.stderr
|
|
1714
|
+
child.stderr?.on("data", (d) => {
|
|
1613
1715
|
const s = d.toString();
|
|
1614
1716
|
stderr += s;
|
|
1615
1717
|
onOutput?.(s, "stderr");
|
|
@@ -1681,7 +1783,7 @@ function ensureRemoteOk(result, command) {
|
|
|
1681
1783
|
}
|
|
1682
1784
|
|
|
1683
1785
|
// src/emulator/lifecycle.ts
|
|
1684
|
-
var PID_FILE =
|
|
1786
|
+
var PID_FILE = path15.join(os7.tmpdir(), "oniro_emulator.pid");
|
|
1685
1787
|
async function attemptHdcConnection(config, address = "127.0.0.1:55555", logger = noopLogger) {
|
|
1686
1788
|
try {
|
|
1687
1789
|
await hdcExec({ config, args: ["start", "-r"], logger });
|
|
@@ -1694,24 +1796,24 @@ async function attemptHdcConnection(config, address = "127.0.0.1:55555", logger
|
|
|
1694
1796
|
}
|
|
1695
1797
|
}
|
|
1696
1798
|
function resolveLauncher(imagesPath, headless, connect) {
|
|
1697
|
-
const runSh =
|
|
1698
|
-
const runBat =
|
|
1799
|
+
const runSh = path15.join(imagesPath, "run.sh");
|
|
1800
|
+
const runBat = path15.join(imagesPath, "run.bat");
|
|
1699
1801
|
const extra = [];
|
|
1700
1802
|
if (headless) extra.push("--headless");
|
|
1701
1803
|
if (connect) extra.push("--connect", connect);
|
|
1702
1804
|
if (os7.platform() === "win32") {
|
|
1703
|
-
if (
|
|
1805
|
+
if (fs15.existsSync(runBat)) {
|
|
1704
1806
|
return { command: "cmd.exe", args: ["/c", runBat, ...extra], cwd: imagesPath };
|
|
1705
1807
|
}
|
|
1706
|
-
if (
|
|
1808
|
+
if (fs15.existsSync(runSh)) {
|
|
1707
1809
|
return { command: "bash", args: [runSh, ...extra], cwd: imagesPath };
|
|
1708
1810
|
}
|
|
1709
1811
|
throw new OniroError(`No emulator launcher (run.bat or run.sh) found in ${imagesPath}.`);
|
|
1710
1812
|
}
|
|
1711
|
-
if (
|
|
1813
|
+
if (fs15.existsSync(runSh)) {
|
|
1712
1814
|
return { command: "bash", args: [runSh, ...extra], cwd: imagesPath };
|
|
1713
1815
|
}
|
|
1714
|
-
if (
|
|
1816
|
+
if (fs15.existsSync(runBat)) {
|
|
1715
1817
|
return { command: "bash", args: [runBat, ...extra], cwd: imagesPath };
|
|
1716
1818
|
}
|
|
1717
1819
|
throw new OniroError(`No emulator launcher (run.sh or run.bat) found in ${imagesPath}.`);
|
|
@@ -1719,9 +1821,9 @@ function resolveLauncher(imagesPath, headless, connect) {
|
|
|
1719
1821
|
async function startEmulator(opts) {
|
|
1720
1822
|
const { config } = opts;
|
|
1721
1823
|
const logger = opts.logger ?? noopLogger;
|
|
1722
|
-
if (os7.platform() !== "win32" &&
|
|
1824
|
+
if (os7.platform() !== "win32" && fs15.existsSync(PID_FILE)) {
|
|
1723
1825
|
try {
|
|
1724
|
-
const pid = Number(
|
|
1826
|
+
const pid = Number(fs15.readFileSync(PID_FILE, "utf8").trim());
|
|
1725
1827
|
if (Number.isFinite(pid) && pid > 0) {
|
|
1726
1828
|
try {
|
|
1727
1829
|
process.kill(pid, 0);
|
|
@@ -1737,13 +1839,13 @@ async function startEmulator(opts) {
|
|
|
1737
1839
|
logger.warn(`Could not read PID file: ${err.message}`);
|
|
1738
1840
|
}
|
|
1739
1841
|
}
|
|
1740
|
-
const imagesPath =
|
|
1741
|
-
if (!
|
|
1842
|
+
const imagesPath = path15.join(getEmulatorDir(config), "images");
|
|
1843
|
+
if (!fs15.existsSync(imagesPath)) {
|
|
1742
1844
|
throw new OniroError(`Emulator images directory not found at ${imagesPath}. Run \`oniro-app emulator install\` first.`);
|
|
1743
1845
|
}
|
|
1744
1846
|
const launcher = resolveLauncher(imagesPath, opts.headless === true, opts.connect);
|
|
1745
1847
|
const logPath = opts.logFile ?? (os7.platform() === "win32" ? "NUL" : "/dev/null");
|
|
1746
|
-
const logFd =
|
|
1848
|
+
const logFd = fs15.openSync(logPath, "a");
|
|
1747
1849
|
let child;
|
|
1748
1850
|
try {
|
|
1749
1851
|
child = (0, import_node_child_process4.spawn)(launcher.command, [...launcher.args], {
|
|
@@ -1753,12 +1855,12 @@ async function startEmulator(opts) {
|
|
|
1753
1855
|
windowsHide: true
|
|
1754
1856
|
});
|
|
1755
1857
|
} finally {
|
|
1756
|
-
|
|
1858
|
+
fs15.closeSync(logFd);
|
|
1757
1859
|
}
|
|
1758
1860
|
if (!child.pid) {
|
|
1759
1861
|
throw new OniroError(`Failed to spawn launcher (${launcher.command} ${launcher.args.join(" ")}).`);
|
|
1760
1862
|
}
|
|
1761
|
-
|
|
1863
|
+
fs15.writeFileSync(PID_FILE, String(child.pid));
|
|
1762
1864
|
child.unref();
|
|
1763
1865
|
logger.info(`Emulator launched (PID ${child.pid}). Logs: ${logPath}`);
|
|
1764
1866
|
const earlyExit = new Promise((resolve4) => {
|
|
@@ -1805,24 +1907,24 @@ ${tail}` : ""}`);
|
|
|
1805
1907
|
}
|
|
1806
1908
|
function readTail(filePath, bytes) {
|
|
1807
1909
|
try {
|
|
1808
|
-
const stat =
|
|
1910
|
+
const stat = fs15.statSync(filePath);
|
|
1809
1911
|
const start = Math.max(0, stat.size - bytes);
|
|
1810
|
-
const fd =
|
|
1912
|
+
const fd = fs15.openSync(filePath, "r");
|
|
1811
1913
|
try {
|
|
1812
1914
|
const buf = Buffer.alloc(stat.size - start);
|
|
1813
|
-
|
|
1915
|
+
fs15.readSync(fd, buf, 0, buf.length, start);
|
|
1814
1916
|
return buf.toString("utf8");
|
|
1815
1917
|
} finally {
|
|
1816
|
-
|
|
1918
|
+
fs15.closeSync(fd);
|
|
1817
1919
|
}
|
|
1818
1920
|
} catch {
|
|
1819
1921
|
return "";
|
|
1820
1922
|
}
|
|
1821
1923
|
}
|
|
1822
1924
|
async function stopEmulator(logger = noopLogger) {
|
|
1823
|
-
if (
|
|
1925
|
+
if (fs15.existsSync(PID_FILE)) {
|
|
1824
1926
|
try {
|
|
1825
|
-
const pid = Number(
|
|
1927
|
+
const pid = Number(fs15.readFileSync(PID_FILE, "utf8").trim());
|
|
1826
1928
|
if (Number.isFinite(pid) && pid > 0) {
|
|
1827
1929
|
try {
|
|
1828
1930
|
process.kill(pid, "SIGTERM");
|
|
@@ -1841,7 +1943,7 @@ async function stopEmulator(logger = noopLogger) {
|
|
|
1841
1943
|
} catch (err) {
|
|
1842
1944
|
logger.warn(`[emulator] could not run ${killCmd}: ${err.message}`);
|
|
1843
1945
|
}
|
|
1844
|
-
|
|
1946
|
+
fs15.rm(PID_FILE, { force: true }, (err) => {
|
|
1845
1947
|
if (err) logger.warn(`Could not remove PID file: ${err.message}`);
|
|
1846
1948
|
});
|
|
1847
1949
|
}
|
|
@@ -2008,21 +2110,20 @@ async function waitForCondition(opts) {
|
|
|
2008
2110
|
}
|
|
2009
2111
|
|
|
2010
2112
|
// src/hdc/app.ts
|
|
2011
|
-
var
|
|
2012
|
-
var
|
|
2013
|
-
var import_node_child_process5 = require("child_process");
|
|
2113
|
+
var fs17 = __toESM(require("fs"), 1);
|
|
2114
|
+
var path17 = __toESM(require("path"), 1);
|
|
2014
2115
|
|
|
2015
2116
|
// src/hdc/project.ts
|
|
2016
|
-
var
|
|
2017
|
-
var
|
|
2117
|
+
var fs16 = __toESM(require("fs"), 1);
|
|
2118
|
+
var path16 = __toESM(require("path"), 1);
|
|
2018
2119
|
var import_json53 = __toESM(require("json5"), 1);
|
|
2019
2120
|
function readJson5(filePath) {
|
|
2020
|
-
const content =
|
|
2121
|
+
const content = fs16.readFileSync(filePath, "utf-8");
|
|
2021
2122
|
return import_json53.default.parse(content);
|
|
2022
2123
|
}
|
|
2023
2124
|
function getBundleName(projectDir) {
|
|
2024
|
-
const appJsonPath =
|
|
2025
|
-
if (!
|
|
2125
|
+
const appJsonPath = path16.join(projectDir, "AppScope", "app.json5");
|
|
2126
|
+
if (!fs16.existsSync(appJsonPath)) {
|
|
2026
2127
|
throw new OniroError(`Could not find app.json5 at ${appJsonPath}`);
|
|
2027
2128
|
}
|
|
2028
2129
|
const appJson = readJson5(appJsonPath);
|
|
@@ -2032,8 +2133,8 @@ function getBundleName(projectDir) {
|
|
|
2032
2133
|
return appJson.app.bundleName;
|
|
2033
2134
|
}
|
|
2034
2135
|
function getMainAbility(projectDir, moduleName = "entry", abilityName) {
|
|
2035
|
-
const moduleJsonPath =
|
|
2036
|
-
if (!
|
|
2136
|
+
const moduleJsonPath = path16.join(projectDir, moduleName, "src", "main", "module.json5");
|
|
2137
|
+
if (!fs16.existsSync(moduleJsonPath)) {
|
|
2037
2138
|
throw new OniroError(`Could not find module.json5 at ${moduleJsonPath}`);
|
|
2038
2139
|
}
|
|
2039
2140
|
const moduleJson = readJson5(moduleJsonPath);
|
|
@@ -2056,11 +2157,16 @@ function getMainAbility(projectDir, moduleName = "entry", abilityName) {
|
|
|
2056
2157
|
async function installApp(opts) {
|
|
2057
2158
|
const logger = scopedLogger(opts.logger ?? noopLogger, "hdc");
|
|
2058
2159
|
const relativeHapPath = opts.hapPath ?? opts.config.get("hapPath", defaultPaths.hapPath);
|
|
2059
|
-
const hapPath =
|
|
2060
|
-
if (!
|
|
2160
|
+
const hapPath = path17.isAbsolute(relativeHapPath) ? relativeHapPath : path17.join(opts.projectDir, relativeHapPath);
|
|
2161
|
+
if (!fs17.existsSync(hapPath)) {
|
|
2061
2162
|
throw new OniroError(`HAP file not found at: ${hapPath}. Build and sign the app first.`);
|
|
2062
2163
|
}
|
|
2063
|
-
const result = await hdcExec({
|
|
2164
|
+
const result = await hdcExec({
|
|
2165
|
+
config: opts.config,
|
|
2166
|
+
args: ["install", hapPath],
|
|
2167
|
+
deviceSerial: opts.deviceSerial,
|
|
2168
|
+
timeoutMs: 3e5
|
|
2169
|
+
});
|
|
2064
2170
|
if (result.stdout.trim()) logger.info(result.stdout.trim());
|
|
2065
2171
|
if (result.stderr.trim()) logger.warn(result.stderr.trim());
|
|
2066
2172
|
ensureOk(result, `hdc install ${hapPath}`);
|
|
@@ -2142,11 +2248,17 @@ function listRunningProcesses(config, options = {}) {
|
|
|
2142
2248
|
const timeoutMs = options.timeoutMs ?? 1e3;
|
|
2143
2249
|
const hdc = getHdcPath(config);
|
|
2144
2250
|
return new Promise((resolve4, reject) => {
|
|
2145
|
-
|
|
2251
|
+
let proc;
|
|
2252
|
+
try {
|
|
2253
|
+
proc = spawnCompat(hdc, ["track-jpid"]);
|
|
2254
|
+
} catch (err) {
|
|
2255
|
+
reject(new OniroError(`Failed to spawn ${hdc}: ${err instanceof Error ? err.message : String(err)}`, err));
|
|
2256
|
+
return;
|
|
2257
|
+
}
|
|
2146
2258
|
const processes = [];
|
|
2147
2259
|
let resolved = false;
|
|
2148
|
-
proc.stdout
|
|
2149
|
-
for (const line of data.toString().split(
|
|
2260
|
+
proc.stdout?.on("data", (data) => {
|
|
2261
|
+
for (const line of data.toString().split(/\r?\n/).filter(Boolean)) {
|
|
2150
2262
|
const match = line.match(/^(\d+)\s+(.+)$/);
|
|
2151
2263
|
if (!match) continue;
|
|
2152
2264
|
const pid = match[1];
|
|
@@ -2154,13 +2266,13 @@ function listRunningProcesses(config, options = {}) {
|
|
|
2154
2266
|
processes.push({ pid, name });
|
|
2155
2267
|
if (options.targetProcessName && name === options.targetProcessName && !resolved) {
|
|
2156
2268
|
resolved = true;
|
|
2157
|
-
proc
|
|
2269
|
+
killProcessTree(proc);
|
|
2158
2270
|
resolve4(pid);
|
|
2159
2271
|
return;
|
|
2160
2272
|
}
|
|
2161
2273
|
}
|
|
2162
2274
|
});
|
|
2163
|
-
proc.stderr
|
|
2275
|
+
proc.stderr?.on("data", (data) => {
|
|
2164
2276
|
logger.warn(`[hdc track-jpid] ${data.toString()}`);
|
|
2165
2277
|
});
|
|
2166
2278
|
proc.on("error", (err) => {
|
|
@@ -2172,7 +2284,7 @@ function listRunningProcesses(config, options = {}) {
|
|
|
2172
2284
|
const timeout = setTimeout(() => {
|
|
2173
2285
|
if (resolved) return;
|
|
2174
2286
|
resolved = true;
|
|
2175
|
-
proc
|
|
2287
|
+
killProcessTree(proc);
|
|
2176
2288
|
if (options.targetProcessName) {
|
|
2177
2289
|
reject(new OniroError(`Could not find process for bundle: ${options.targetProcessName}`));
|
|
2178
2290
|
} else {
|
|
@@ -2330,12 +2442,12 @@ async function reboot(opts) {
|
|
|
2330
2442
|
}
|
|
2331
2443
|
|
|
2332
2444
|
// src/hdc/screen.ts
|
|
2333
|
-
var
|
|
2445
|
+
var fs18 = __toESM(require("fs"), 1);
|
|
2334
2446
|
var os8 = __toESM(require("os"), 1);
|
|
2335
|
-
var
|
|
2447
|
+
var path18 = __toESM(require("path"), 1);
|
|
2336
2448
|
var import_node_crypto = require("crypto");
|
|
2337
2449
|
function tmpName(ext) {
|
|
2338
|
-
return
|
|
2450
|
+
return path18.join(os8.tmpdir(), `oniro_screen_${(0, import_node_crypto.randomBytes)(6).toString("hex")}.${ext}`);
|
|
2339
2451
|
}
|
|
2340
2452
|
async function takeScreenshot(opts) {
|
|
2341
2453
|
const remote = "/data/local/tmp/oniro_screenshot.jpeg";
|
|
@@ -2345,11 +2457,11 @@ async function takeScreenshot(opts) {
|
|
|
2345
2457
|
const local = tmpName("jpeg");
|
|
2346
2458
|
try {
|
|
2347
2459
|
await recvFile({ config: opts.config, remote, local, deviceSerial: opts.deviceSerial, timeoutMs });
|
|
2348
|
-
const pixels = await
|
|
2460
|
+
const pixels = await fs18.promises.readFile(local);
|
|
2349
2461
|
const size = await getDisplaySize(opts.config, { deviceSerial: opts.deviceSerial }) ?? { width: 0, height: 0 };
|
|
2350
2462
|
return { pixels, width: size.width, height: size.height };
|
|
2351
2463
|
} finally {
|
|
2352
|
-
|
|
2464
|
+
fs18.promises.unlink(local).catch(() => {
|
|
2353
2465
|
});
|
|
2354
2466
|
void shell({ config: opts.config, command: `rm -f ${remote}`, deviceSerial: opts.deviceSerial }).catch(() => {
|
|
2355
2467
|
});
|
|
@@ -2368,9 +2480,9 @@ async function captureBurst(opts) {
|
|
|
2368
2480
|
const local = tmpName(`burst_${i}.jpeg`);
|
|
2369
2481
|
try {
|
|
2370
2482
|
await recvFile({ config: opts.config, remote: `${remoteDir}/f${i}.jpeg`, local, deviceSerial: opts.deviceSerial });
|
|
2371
|
-
frames.push(await
|
|
2483
|
+
frames.push(await fs18.promises.readFile(local));
|
|
2372
2484
|
} finally {
|
|
2373
|
-
|
|
2485
|
+
fs18.promises.unlink(local).catch(() => {
|
|
2374
2486
|
});
|
|
2375
2487
|
}
|
|
2376
2488
|
}
|
|
@@ -2496,9 +2608,9 @@ async function sendRawTouch(opts) {
|
|
|
2496
2608
|
}
|
|
2497
2609
|
|
|
2498
2610
|
// src/hdc/dump.ts
|
|
2499
|
-
var
|
|
2611
|
+
var fs19 = __toESM(require("fs"), 1);
|
|
2500
2612
|
var os9 = __toESM(require("os"), 1);
|
|
2501
|
-
var
|
|
2613
|
+
var path19 = __toESM(require("path"), 1);
|
|
2502
2614
|
var import_node_crypto2 = require("crypto");
|
|
2503
2615
|
function parseBounds(s) {
|
|
2504
2616
|
const m = /^\[(-?\d+),(-?\d+)\]\[(-?\d+),(-?\d+)\]$/.exec(s ?? "");
|
|
@@ -2583,10 +2695,10 @@ async function dumpLayout(opts) {
|
|
|
2583
2695
|
if (!m) throw new OniroError(`Could not parse dumpLayout output:
|
|
2584
2696
|
${dump.stdout}`);
|
|
2585
2697
|
const remote = m[1];
|
|
2586
|
-
const local =
|
|
2698
|
+
const local = path19.join(os9.tmpdir(), `oniro_layout_${(0, import_node_crypto2.randomBytes)(6).toString("hex")}.json`);
|
|
2587
2699
|
try {
|
|
2588
2700
|
await recvFile({ config: opts.config, remote, local, deviceSerial: opts.deviceSerial, timeoutMs });
|
|
2589
|
-
const parsed = JSON.parse(await
|
|
2701
|
+
const parsed = JSON.parse(await fs19.promises.readFile(local, "utf8"));
|
|
2590
2702
|
const display = await getDisplaySize(opts.config, { deviceSerial: opts.deviceSerial });
|
|
2591
2703
|
if (opts.prune === false) {
|
|
2592
2704
|
return { display, tree: parsed };
|
|
@@ -2595,7 +2707,7 @@ ${dump.stdout}`);
|
|
|
2595
2707
|
const tree = Array.isArray(prunedRaw) ? { children: prunedRaw } : prunedRaw ?? { children: [] };
|
|
2596
2708
|
return { display, tree };
|
|
2597
2709
|
} finally {
|
|
2598
|
-
|
|
2710
|
+
fs19.promises.unlink(local).catch(() => {
|
|
2599
2711
|
});
|
|
2600
2712
|
void shell({ config: opts.config, command: `rm -f ${remote}`, deviceSerial: opts.deviceSerial }).catch(() => {
|
|
2601
2713
|
});
|
|
@@ -2603,14 +2715,21 @@ ${dump.stdout}`);
|
|
|
2603
2715
|
}
|
|
2604
2716
|
|
|
2605
2717
|
// src/hdc/hilog.ts
|
|
2606
|
-
var import_node_child_process6 = require("child_process");
|
|
2607
2718
|
function setHilogLevel(opts) {
|
|
2608
2719
|
const logger = opts.logger ?? noopLogger;
|
|
2609
2720
|
const hdc = getHdcPath(opts.config);
|
|
2610
2721
|
return new Promise((resolve4, reject) => {
|
|
2611
|
-
|
|
2722
|
+
let child;
|
|
2723
|
+
try {
|
|
2724
|
+
child = spawnCompat(hdc, ["shell", "hilog", "-b", opts.level]);
|
|
2725
|
+
} catch (err) {
|
|
2726
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2727
|
+
logger.error(`[hilog] failed to spawn hdc: ${msg}`);
|
|
2728
|
+
reject(err instanceof Error ? err : new Error(msg));
|
|
2729
|
+
return;
|
|
2730
|
+
}
|
|
2612
2731
|
let stderr = "";
|
|
2613
|
-
child.stderr
|
|
2732
|
+
child.stderr?.on("data", (chunk) => {
|
|
2614
2733
|
stderr += chunk.toString();
|
|
2615
2734
|
});
|
|
2616
2735
|
child.once("error", (err) => {
|
|
@@ -2639,7 +2758,7 @@ function streamHilog(opts) {
|
|
|
2639
2758
|
if (opts.domain !== void 0 && `${opts.domain}` !== "") {
|
|
2640
2759
|
args.push("-D", `${opts.domain}`);
|
|
2641
2760
|
}
|
|
2642
|
-
return (
|
|
2761
|
+
return spawnCompat(hdc, args);
|
|
2643
2762
|
}
|
|
2644
2763
|
var HILOG_LINE_RE = /^(\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3})\s+(\d+)\s+(\d+)\s+([DIWEF])\s+(\S+):\s+(.*)$/;
|
|
2645
2764
|
function parseHilogLine(line) {
|
|
@@ -2685,10 +2804,7 @@ function waitForLog(opts) {
|
|
|
2685
2804
|
if (reconnectTimer) clearTimeout(reconnectTimer);
|
|
2686
2805
|
opts.abortSignal?.removeEventListener("abort", onAbort);
|
|
2687
2806
|
if (child) {
|
|
2688
|
-
|
|
2689
|
-
child.kill("SIGKILL");
|
|
2690
|
-
} catch {
|
|
2691
|
-
}
|
|
2807
|
+
killProcessTree(child);
|
|
2692
2808
|
child = null;
|
|
2693
2809
|
}
|
|
2694
2810
|
};
|
|
@@ -2704,6 +2820,7 @@ function waitForLog(opts) {
|
|
|
2704
2820
|
cleanup();
|
|
2705
2821
|
reject(err);
|
|
2706
2822
|
};
|
|
2823
|
+
const failWith = (err) => fail(err instanceof Error ? err : new Error(String(err)));
|
|
2707
2824
|
const onAbort = () => fail(new CancelledError("waitForLog cancelled."));
|
|
2708
2825
|
const timer = setTimeout(
|
|
2709
2826
|
() => fail(new OniroError(`No log line matched ${String(opts.pattern)} within ${opts.timeoutMs}ms.`)),
|
|
@@ -2718,10 +2835,9 @@ function waitForLog(opts) {
|
|
|
2718
2835
|
child = stream;
|
|
2719
2836
|
stream.stdout.on("data", (d) => {
|
|
2720
2837
|
buf += d.toString();
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
buf = buf.slice(idx + 1);
|
|
2838
|
+
const lines = buf.split(/\r?\n/);
|
|
2839
|
+
buf = lines.pop() ?? "";
|
|
2840
|
+
for (const line of lines) {
|
|
2725
2841
|
const entry = parseHilogLine(line);
|
|
2726
2842
|
if (entry && opts.pattern.test(entryText(entry))) {
|
|
2727
2843
|
finish(entry);
|
|
@@ -2733,13 +2849,13 @@ function waitForLog(opts) {
|
|
|
2733
2849
|
if (settled || child !== stream) return;
|
|
2734
2850
|
child = null;
|
|
2735
2851
|
if (Date.now() < deadline) {
|
|
2736
|
-
reconnectTimer = setTimeout(() => void spawnStream(), 1e3);
|
|
2852
|
+
reconnectTimer = setTimeout(() => void spawnStream().catch(failWith), 1e3);
|
|
2737
2853
|
}
|
|
2738
2854
|
};
|
|
2739
2855
|
stream.once("close", onEnd);
|
|
2740
2856
|
stream.once("error", onEnd);
|
|
2741
2857
|
};
|
|
2742
|
-
void spawnStream();
|
|
2858
|
+
void spawnStream().catch(failWith);
|
|
2743
2859
|
});
|
|
2744
2860
|
}
|
|
2745
2861
|
function watchLog(opts) {
|
|
@@ -2752,10 +2868,7 @@ function watchLog(opts) {
|
|
|
2752
2868
|
clearTimeout(timer);
|
|
2753
2869
|
opts.abortSignal?.removeEventListener("abort", onAbort);
|
|
2754
2870
|
if (child) {
|
|
2755
|
-
|
|
2756
|
-
child.kill("SIGKILL");
|
|
2757
|
-
} catch {
|
|
2758
|
-
}
|
|
2871
|
+
killProcessTree(child);
|
|
2759
2872
|
child = null;
|
|
2760
2873
|
}
|
|
2761
2874
|
};
|
|
@@ -2780,15 +2893,19 @@ function watchLog(opts) {
|
|
|
2780
2893
|
child = stream;
|
|
2781
2894
|
stream.stdout.on("data", (d) => {
|
|
2782
2895
|
buf += d.toString();
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
buf = buf.slice(idx + 1);
|
|
2896
|
+
const lines = buf.split(/\r?\n/);
|
|
2897
|
+
buf = lines.pop() ?? "";
|
|
2898
|
+
for (const line of lines) {
|
|
2787
2899
|
const entry = parseHilogLine(line);
|
|
2788
2900
|
if (entry && opts.pattern.test(entryText(entry))) matches.push(entry);
|
|
2789
2901
|
}
|
|
2790
2902
|
});
|
|
2791
|
-
})()
|
|
2903
|
+
})().catch((err) => {
|
|
2904
|
+
if (settled) return;
|
|
2905
|
+
settled = true;
|
|
2906
|
+
cleanup();
|
|
2907
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
2908
|
+
});
|
|
2792
2909
|
});
|
|
2793
2910
|
}
|
|
2794
2911
|
async function dumpLog(opts) {
|
|
@@ -2811,13 +2928,12 @@ async function dumpLog(opts) {
|
|
|
2811
2928
|
timeoutMs: opts.timeoutMs ?? 3e4,
|
|
2812
2929
|
logger: opts.logger
|
|
2813
2930
|
});
|
|
2814
|
-
return res.stdout.split(
|
|
2931
|
+
return res.stdout.split(/\r?\n/).map((line) => parseHilogLine(line)).filter((e) => e !== null);
|
|
2815
2932
|
}
|
|
2816
2933
|
|
|
2817
2934
|
// src/build/runHvigorw.ts
|
|
2818
|
-
var
|
|
2819
|
-
var
|
|
2820
|
-
var import_node_child_process7 = require("child_process");
|
|
2935
|
+
var fs20 = __toESM(require("fs"), 1);
|
|
2936
|
+
var path20 = __toESM(require("path"), 1);
|
|
2821
2937
|
function buildHvigorwArgs(opts) {
|
|
2822
2938
|
const args = [opts.task ?? "assembleHap", "--mode", "module"];
|
|
2823
2939
|
args.push("-p", `product=${opts.product ?? "default"}`);
|
|
@@ -2829,19 +2945,23 @@ function buildHvigorwArgs(opts) {
|
|
|
2829
2945
|
return args;
|
|
2830
2946
|
}
|
|
2831
2947
|
function runHvigorw(opts) {
|
|
2832
|
-
const { config
|
|
2948
|
+
const { config } = opts;
|
|
2949
|
+
const projectDir = toLongPath(opts.projectDir);
|
|
2833
2950
|
const logger = opts.logger ?? noopLogger;
|
|
2834
|
-
if (!
|
|
2951
|
+
if (!fs20.existsSync(path20.join(projectDir, "build-profile.json5"))) {
|
|
2835
2952
|
throw new OniroError(`Not an OpenHarmony project: ${projectDir} (build-profile.json5 not found).`);
|
|
2836
2953
|
}
|
|
2837
|
-
if (!
|
|
2954
|
+
if (!fs20.existsSync(getCmdToolsPath(config))) {
|
|
2838
2955
|
throw new CmdToolsNotInstalledError(getCmdToolsPath(config));
|
|
2839
2956
|
}
|
|
2840
2957
|
const hvigorw = getHvigorwPath(config, projectDir);
|
|
2958
|
+
if (!fs20.existsSync(hvigorw)) {
|
|
2959
|
+
throw new OniroError(`hvigorw wrapper not found at ${hvigorw}.`);
|
|
2960
|
+
}
|
|
2841
2961
|
const args = buildHvigorwArgs(opts);
|
|
2842
2962
|
if (process.platform !== "win32") {
|
|
2843
2963
|
try {
|
|
2844
|
-
|
|
2964
|
+
fs20.chmodSync(hvigorw, 493);
|
|
2845
2965
|
} catch {
|
|
2846
2966
|
}
|
|
2847
2967
|
}
|
|
@@ -2855,26 +2975,29 @@ function runHvigorw(opts) {
|
|
|
2855
2975
|
return;
|
|
2856
2976
|
}
|
|
2857
2977
|
logger.info(`[build] ${hvigorw} ${args.join(" ")}`);
|
|
2858
|
-
|
|
2978
|
+
let child;
|
|
2979
|
+
try {
|
|
2980
|
+
child = spawnCompat(hvigorw, args, { cwd: projectDir, env });
|
|
2981
|
+
} catch (err) {
|
|
2982
|
+
reject(new OniroError(`Failed to start hvigorw: ${err instanceof Error ? err.message : String(err)}`, err));
|
|
2983
|
+
return;
|
|
2984
|
+
}
|
|
2859
2985
|
let settled = false;
|
|
2860
2986
|
const cleanup = () => opts.abortSignal?.removeEventListener("abort", onAbort);
|
|
2861
2987
|
const onAbort = () => {
|
|
2862
2988
|
if (settled) return;
|
|
2863
2989
|
settled = true;
|
|
2864
2990
|
cleanup();
|
|
2865
|
-
|
|
2866
|
-
child.kill("SIGKILL");
|
|
2867
|
-
} catch {
|
|
2868
|
-
}
|
|
2991
|
+
killProcessTree(child);
|
|
2869
2992
|
reject(new CancelledError("hvigorw build cancelled."));
|
|
2870
2993
|
};
|
|
2871
2994
|
opts.abortSignal?.addEventListener("abort", onAbort, { once: true });
|
|
2872
|
-
child.stdout
|
|
2995
|
+
child.stdout?.on("data", (chunk) => {
|
|
2873
2996
|
const text = chunk.toString();
|
|
2874
2997
|
opts.onOutput?.(text, "stdout");
|
|
2875
2998
|
logger.info(text.trimEnd());
|
|
2876
2999
|
});
|
|
2877
|
-
child.stderr
|
|
3000
|
+
child.stderr?.on("data", (chunk) => {
|
|
2878
3001
|
const text = chunk.toString();
|
|
2879
3002
|
opts.onOutput?.(text, "stderr");
|
|
2880
3003
|
logger.warn(text.trimEnd());
|
|
@@ -2897,8 +3020,8 @@ function runHvigorw(opts) {
|
|
|
2897
3020
|
}
|
|
2898
3021
|
|
|
2899
3022
|
// src/build/discoverHaps.ts
|
|
2900
|
-
var
|
|
2901
|
-
var
|
|
3023
|
+
var fs21 = __toESM(require("fs"), 1);
|
|
3024
|
+
var path21 = __toESM(require("path"), 1);
|
|
2902
3025
|
function moduleOfHap(hapFileName) {
|
|
2903
3026
|
const dash = hapFileName.indexOf("-");
|
|
2904
3027
|
return dash > 0 ? hapFileName.slice(0, dash) : hapFileName.replace(/\.hap$/i, "");
|
|
@@ -2909,9 +3032,9 @@ async function discoverHaps(opts) {
|
|
|
2909
3032
|
const result = {};
|
|
2910
3033
|
const walk = async (dir, depth) => {
|
|
2911
3034
|
if (depth > maxDepth) return;
|
|
2912
|
-
const entries = await
|
|
3035
|
+
const entries = await fs21.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
2913
3036
|
for (const e of entries) {
|
|
2914
|
-
const full =
|
|
3037
|
+
const full = path21.join(dir, e.name);
|
|
2915
3038
|
if (e.isDirectory()) {
|
|
2916
3039
|
if (e.name === "node_modules" || e.name === "oh_modules" || e.name === ".git") continue;
|
|
2917
3040
|
await walk(full, depth + 1);
|
|
@@ -2928,8 +3051,8 @@ async function discoverHaps(opts) {
|
|
|
2928
3051
|
}
|
|
2929
3052
|
|
|
2930
3053
|
// src/build/ohpm.ts
|
|
2931
|
-
var
|
|
2932
|
-
var
|
|
3054
|
+
var fs22 = __toESM(require("fs"), 1);
|
|
3055
|
+
var path22 = __toESM(require("path"), 1);
|
|
2933
3056
|
function runOhpm(opts) {
|
|
2934
3057
|
return runProcess({
|
|
2935
3058
|
command: getOhpmPath(opts.config),
|
|
@@ -2942,7 +3065,7 @@ function runOhpm(opts) {
|
|
|
2942
3065
|
});
|
|
2943
3066
|
}
|
|
2944
3067
|
async function ensureOhModules(opts) {
|
|
2945
|
-
if (
|
|
3068
|
+
if (fs22.existsSync(path22.join(opts.projectDir, "oh_modules"))) {
|
|
2946
3069
|
return { installed: false };
|
|
2947
3070
|
}
|
|
2948
3071
|
const res = await runOhpm({
|
|
@@ -2994,10 +3117,11 @@ async function buildHap(opts) {
|
|
|
2994
3117
|
const logger = scopedLogger(opts.logger ?? noopLogger, "build");
|
|
2995
3118
|
const warnings = [];
|
|
2996
3119
|
const start = Date.now();
|
|
3120
|
+
const projectDir = toLongPath(opts.projectDir);
|
|
2997
3121
|
if (opts.autoInstallDeps !== false) {
|
|
2998
3122
|
const r = await ensureOhModules({
|
|
2999
3123
|
config: opts.config,
|
|
3000
|
-
projectDir
|
|
3124
|
+
projectDir,
|
|
3001
3125
|
abortSignal: opts.abortSignal,
|
|
3002
3126
|
onOutput: opts.onOutput,
|
|
3003
3127
|
logger: opts.logger
|
|
@@ -3006,7 +3130,7 @@ async function buildHap(opts) {
|
|
|
3006
3130
|
}
|
|
3007
3131
|
const { exitCode } = await runHvigorw({
|
|
3008
3132
|
config: opts.config,
|
|
3009
|
-
projectDir
|
|
3133
|
+
projectDir,
|
|
3010
3134
|
product: opts.product,
|
|
3011
3135
|
module: opts.module,
|
|
3012
3136
|
buildMode: opts.mode,
|
|
@@ -3016,7 +3140,7 @@ async function buildHap(opts) {
|
|
|
3016
3140
|
onOutput: opts.onOutput,
|
|
3017
3141
|
logger: opts.logger
|
|
3018
3142
|
});
|
|
3019
|
-
const discoveredHaps = await discoverHaps({ projectDir
|
|
3143
|
+
const discoveredHaps = await discoverHaps({ projectDir });
|
|
3020
3144
|
const anySigned = Object.values(discoveredHaps).some((m) => m.signed.length > 0);
|
|
3021
3145
|
if (!anySigned) {
|
|
3022
3146
|
warnings.push(
|
|
@@ -3037,21 +3161,21 @@ function isValidBundleName(bundleName) {
|
|
|
3037
3161
|
}
|
|
3038
3162
|
|
|
3039
3163
|
// src/project/templates.ts
|
|
3040
|
-
var
|
|
3041
|
-
var
|
|
3164
|
+
var fs23 = __toESM(require("fs"), 1);
|
|
3165
|
+
var path23 = __toESM(require("path"), 1);
|
|
3042
3166
|
function toHumanTemplateName(folderName) {
|
|
3043
3167
|
return folderName.replace(/([a-z])([A-Z])/g, "$1 $2");
|
|
3044
3168
|
}
|
|
3045
3169
|
function listTemplates(templateRoot) {
|
|
3046
|
-
if (!
|
|
3047
|
-
const entries =
|
|
3170
|
+
if (!fs23.existsSync(templateRoot)) return [];
|
|
3171
|
+
const entries = fs23.readdirSync(templateRoot, { withFileTypes: true });
|
|
3048
3172
|
return entries.filter((e) => e.isDirectory()).map((e) => {
|
|
3049
|
-
const dir =
|
|
3050
|
-
const metaPath =
|
|
3173
|
+
const dir = path23.join(templateRoot, e.name);
|
|
3174
|
+
const metaPath = path23.join(dir, "template.json");
|
|
3051
3175
|
let meta = {};
|
|
3052
|
-
if (
|
|
3176
|
+
if (fs23.existsSync(metaPath)) {
|
|
3053
3177
|
try {
|
|
3054
|
-
meta = JSON.parse(
|
|
3178
|
+
meta = JSON.parse(fs23.readFileSync(metaPath, "utf8"));
|
|
3055
3179
|
} catch {
|
|
3056
3180
|
}
|
|
3057
3181
|
}
|
|
@@ -3066,57 +3190,57 @@ function listTemplates(templateRoot) {
|
|
|
3066
3190
|
function validateTemplateLayout(templateDir, defaultModuleName) {
|
|
3067
3191
|
const required = [
|
|
3068
3192
|
"build-profile.json5",
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3193
|
+
path23.join("AppScope", "app.json5"),
|
|
3194
|
+
path23.join(defaultModuleName, "src", "main", "module.json5"),
|
|
3195
|
+
path23.join(defaultModuleName, "oh-package.json5"),
|
|
3072
3196
|
"hvigorfile.ts"
|
|
3073
3197
|
];
|
|
3074
|
-
return required.filter((rel) => !
|
|
3198
|
+
return required.filter((rel) => !fs23.existsSync(path23.join(templateDir, rel)));
|
|
3075
3199
|
}
|
|
3076
3200
|
|
|
3077
3201
|
// src/project/createScaffold.ts
|
|
3078
|
-
var
|
|
3079
|
-
var
|
|
3202
|
+
var fs25 = __toESM(require("fs"), 1);
|
|
3203
|
+
var path24 = __toESM(require("path"), 1);
|
|
3080
3204
|
|
|
3081
3205
|
// src/project/jsonHelpers.ts
|
|
3082
|
-
var
|
|
3206
|
+
var fs24 = __toESM(require("fs"), 1);
|
|
3083
3207
|
var import_json54 = __toESM(require("json5"), 1);
|
|
3084
3208
|
function readJson5File(filePath) {
|
|
3085
|
-
return import_json54.default.parse(
|
|
3209
|
+
return import_json54.default.parse(fs24.readFileSync(filePath, "utf8"));
|
|
3086
3210
|
}
|
|
3087
3211
|
function writeJson5File(filePath, value) {
|
|
3088
|
-
|
|
3212
|
+
fs24.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
|
|
3089
3213
|
}
|
|
3090
3214
|
function readJsonFile(filePath) {
|
|
3091
|
-
return JSON.parse(
|
|
3215
|
+
return JSON.parse(fs24.readFileSync(filePath, "utf8"));
|
|
3092
3216
|
}
|
|
3093
3217
|
function writeJsonFile(filePath, value) {
|
|
3094
|
-
|
|
3218
|
+
fs24.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
|
|
3095
3219
|
}
|
|
3096
3220
|
|
|
3097
3221
|
// src/project/createScaffold.ts
|
|
3098
3222
|
var IGNORED_DIRS = /* @__PURE__ */ new Set(["oh_modules", "node_modules", "build", ".hvigor"]);
|
|
3099
3223
|
async function pathExists(p) {
|
|
3100
3224
|
try {
|
|
3101
|
-
await
|
|
3225
|
+
await fs25.promises.access(p);
|
|
3102
3226
|
return true;
|
|
3103
3227
|
} catch {
|
|
3104
3228
|
return false;
|
|
3105
3229
|
}
|
|
3106
3230
|
}
|
|
3107
3231
|
async function copyDirRecursive(srcDir, destDir) {
|
|
3108
|
-
await
|
|
3109
|
-
const entries = await
|
|
3232
|
+
await fs25.promises.mkdir(destDir, { recursive: true });
|
|
3233
|
+
const entries = await fs25.promises.readdir(srcDir, { withFileTypes: true });
|
|
3110
3234
|
for (const entry of entries) {
|
|
3111
|
-
const src =
|
|
3112
|
-
const dest =
|
|
3235
|
+
const src = path24.join(srcDir, entry.name);
|
|
3236
|
+
const dest = path24.join(destDir, entry.name);
|
|
3113
3237
|
if (entry.isDirectory()) {
|
|
3114
3238
|
await copyDirRecursive(src, dest);
|
|
3115
3239
|
} else if (entry.isSymbolicLink()) {
|
|
3116
3240
|
continue;
|
|
3117
3241
|
} else if (entry.isFile()) {
|
|
3118
3242
|
if (entry.name === "template.json") continue;
|
|
3119
|
-
await
|
|
3243
|
+
await fs25.promises.copyFile(src, dest);
|
|
3120
3244
|
}
|
|
3121
3245
|
}
|
|
3122
3246
|
}
|
|
@@ -3124,9 +3248,9 @@ async function normalizeJson5ToJson(projectDir, logger) {
|
|
|
3124
3248
|
const stack = [projectDir];
|
|
3125
3249
|
while (stack.length > 0) {
|
|
3126
3250
|
const currentDir = stack.pop();
|
|
3127
|
-
const entries = await
|
|
3251
|
+
const entries = await fs25.promises.readdir(currentDir, { withFileTypes: true });
|
|
3128
3252
|
for (const entry of entries) {
|
|
3129
|
-
const fullPath =
|
|
3253
|
+
const fullPath = path24.join(currentDir, entry.name);
|
|
3130
3254
|
if (entry.isDirectory()) {
|
|
3131
3255
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
3132
3256
|
stack.push(fullPath);
|
|
@@ -3148,23 +3272,23 @@ function toJavaPropertiesPath(p) {
|
|
|
3148
3272
|
function createOrUpdateLocalProperties(projectDir, sdkDir) {
|
|
3149
3273
|
const content = `sdk.dir=${toJavaPropertiesPath(sdkDir)}
|
|
3150
3274
|
`;
|
|
3151
|
-
|
|
3275
|
+
fs25.writeFileSync(path24.join(projectDir, "local.properties"), content, "utf8");
|
|
3152
3276
|
}
|
|
3153
3277
|
function renameIfExists(fromPath, toPath) {
|
|
3154
|
-
if (
|
|
3155
|
-
|
|
3278
|
+
if (fs25.existsSync(fromPath) && fromPath !== toPath) {
|
|
3279
|
+
fs25.renameSync(fromPath, toPath);
|
|
3156
3280
|
}
|
|
3157
3281
|
}
|
|
3158
3282
|
function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
|
|
3159
|
-
const appJsonPath =
|
|
3160
|
-
if (
|
|
3283
|
+
const appJsonPath = path24.join(projectDir, "AppScope", "app.json5");
|
|
3284
|
+
if (fs25.existsSync(appJsonPath)) {
|
|
3161
3285
|
const appJson = readJson5File(appJsonPath);
|
|
3162
3286
|
appJson.app = appJson.app ?? {};
|
|
3163
3287
|
appJson.app.bundleName = args.bundleName;
|
|
3164
3288
|
writeJson5File(appJsonPath, appJson);
|
|
3165
3289
|
}
|
|
3166
|
-
const stringsPath =
|
|
3167
|
-
if (
|
|
3290
|
+
const stringsPath = path24.join(projectDir, "AppScope", "resources", "base", "element", "string.json");
|
|
3291
|
+
if (fs25.existsSync(stringsPath)) {
|
|
3168
3292
|
const strings = readJsonFile(stringsPath);
|
|
3169
3293
|
if (Array.isArray(strings.string)) {
|
|
3170
3294
|
const appName = strings.string.find((s) => s.name === "app_name");
|
|
@@ -3172,8 +3296,8 @@ function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
|
|
|
3172
3296
|
}
|
|
3173
3297
|
writeJsonFile(stringsPath, strings);
|
|
3174
3298
|
}
|
|
3175
|
-
const buildProfilePath =
|
|
3176
|
-
if (
|
|
3299
|
+
const buildProfilePath = path24.join(projectDir, "build-profile.json5");
|
|
3300
|
+
if (fs25.existsSync(buildProfilePath)) {
|
|
3177
3301
|
const buildProfile = readJson5File(buildProfilePath);
|
|
3178
3302
|
buildProfile.app = buildProfile.app ?? {};
|
|
3179
3303
|
if (Array.isArray(buildProfile.app.products) && buildProfile.app.products.length > 0) {
|
|
@@ -3186,23 +3310,23 @@ function updateTemplateConfigs(projectDir, args, sdkBaseDir) {
|
|
|
3186
3310
|
}
|
|
3187
3311
|
writeJson5File(buildProfilePath, buildProfile);
|
|
3188
3312
|
}
|
|
3189
|
-
const moduleJsonPath =
|
|
3190
|
-
if (
|
|
3313
|
+
const moduleJsonPath = path24.join(projectDir, args.moduleName, "src", "main", "module.json5");
|
|
3314
|
+
if (fs25.existsSync(moduleJsonPath)) {
|
|
3191
3315
|
const moduleJson = readJson5File(moduleJsonPath);
|
|
3192
3316
|
moduleJson.module = moduleJson.module ?? {};
|
|
3193
3317
|
moduleJson.module.name = args.moduleName;
|
|
3194
3318
|
writeJson5File(moduleJsonPath, moduleJson);
|
|
3195
3319
|
}
|
|
3196
|
-
const moduleOhPackagePath =
|
|
3197
|
-
if (
|
|
3320
|
+
const moduleOhPackagePath = path24.join(projectDir, args.moduleName, "oh-package.json5");
|
|
3321
|
+
if (fs25.existsSync(moduleOhPackagePath)) {
|
|
3198
3322
|
const pkg = readJson5File(moduleOhPackagePath);
|
|
3199
3323
|
pkg.name = args.moduleName;
|
|
3200
3324
|
writeJson5File(moduleOhPackagePath, pkg);
|
|
3201
3325
|
}
|
|
3202
|
-
const vscodeDir =
|
|
3203
|
-
|
|
3326
|
+
const vscodeDir = path24.join(projectDir, ".vscode");
|
|
3327
|
+
fs25.mkdirSync(vscodeDir, { recursive: true });
|
|
3204
3328
|
const hapPath = `${args.moduleName}/build/default/outputs/default/${args.moduleName}-default-signed.hap`;
|
|
3205
|
-
writeJsonFile(
|
|
3329
|
+
writeJsonFile(path24.join(vscodeDir, "settings.json"), {
|
|
3206
3330
|
"oniro.hapPath": hapPath,
|
|
3207
3331
|
"files.associations": { "*.json5": "jsonc" }
|
|
3208
3332
|
});
|
|
@@ -3219,7 +3343,7 @@ async function createScaffold(opts) {
|
|
|
3219
3343
|
if (!opts.location || !await pathExists(opts.location)) {
|
|
3220
3344
|
throw new OniroError(`Location does not exist: ${opts.location}`);
|
|
3221
3345
|
}
|
|
3222
|
-
const templateDir =
|
|
3346
|
+
const templateDir = path24.join(opts.templateRoot, opts.templateId);
|
|
3223
3347
|
if (!await pathExists(templateDir)) {
|
|
3224
3348
|
throw new OniroError(`Template not found: ${templateDir}`);
|
|
3225
3349
|
}
|
|
@@ -3232,17 +3356,17 @@ async function createScaffold(opts) {
|
|
|
3232
3356
|
throw new OniroError(`Template '${opts.templateId}' is missing required files:
|
|
3233
3357
|
- ${missing.join("\n- ")}`);
|
|
3234
3358
|
}
|
|
3235
|
-
const projectDir =
|
|
3359
|
+
const projectDir = path24.join(opts.location, opts.projectName);
|
|
3236
3360
|
if (await pathExists(projectDir)) {
|
|
3237
3361
|
if (!opts.overwrite) {
|
|
3238
3362
|
throw new OniroError(`Destination already exists: ${projectDir}. Pass overwrite: true to replace it.`);
|
|
3239
3363
|
}
|
|
3240
|
-
await
|
|
3364
|
+
await fs25.promises.rm(projectDir, { recursive: true, force: true });
|
|
3241
3365
|
}
|
|
3242
3366
|
logger.info(`[create] Scaffolding ${opts.templateId} at ${projectDir}`);
|
|
3243
3367
|
await copyDirRecursive(templateDir, projectDir);
|
|
3244
3368
|
if (moduleName !== defaultModuleName) {
|
|
3245
|
-
renameIfExists(
|
|
3369
|
+
renameIfExists(path24.join(projectDir, defaultModuleName), path24.join(projectDir, moduleName));
|
|
3246
3370
|
}
|
|
3247
3371
|
updateTemplateConfigs(
|
|
3248
3372
|
projectDir,
|
|
@@ -3259,11 +3383,11 @@ async function createScaffold(opts) {
|
|
|
3259
3383
|
}
|
|
3260
3384
|
|
|
3261
3385
|
// src/project/listModules.ts
|
|
3262
|
-
var
|
|
3263
|
-
var
|
|
3386
|
+
var fs26 = __toESM(require("fs"), 1);
|
|
3387
|
+
var path25 = __toESM(require("path"), 1);
|
|
3264
3388
|
function listModules(opts) {
|
|
3265
|
-
const buildProfile =
|
|
3266
|
-
if (!
|
|
3389
|
+
const buildProfile = path25.join(opts.projectDir, "build-profile.json5");
|
|
3390
|
+
if (!fs26.existsSync(buildProfile)) {
|
|
3267
3391
|
throw new OniroError(`build-profile.json5 not found at ${buildProfile}`);
|
|
3268
3392
|
}
|
|
3269
3393
|
const parsed = readJson5File(buildProfile);
|
|
@@ -3275,12 +3399,12 @@ function listModules(opts) {
|
|
|
3275
3399
|
}
|
|
3276
3400
|
|
|
3277
3401
|
// src/project/abilities.ts
|
|
3278
|
-
var
|
|
3279
|
-
var
|
|
3402
|
+
var fs27 = __toESM(require("fs"), 1);
|
|
3403
|
+
var path26 = __toESM(require("path"), 1);
|
|
3280
3404
|
function listAbilities(opts) {
|
|
3281
3405
|
const moduleName = opts.moduleName ?? "entry";
|
|
3282
|
-
const moduleJsonPath =
|
|
3283
|
-
if (!
|
|
3406
|
+
const moduleJsonPath = path26.join(opts.projectDir, moduleName, "src", "main", "module.json5");
|
|
3407
|
+
if (!fs27.existsSync(moduleJsonPath)) {
|
|
3284
3408
|
throw new OniroError(`Could not find module.json5 at ${moduleJsonPath}`);
|
|
3285
3409
|
}
|
|
3286
3410
|
const parsed = readJson5File(moduleJsonPath);
|
|
@@ -3288,12 +3412,12 @@ function listAbilities(opts) {
|
|
|
3288
3412
|
}
|
|
3289
3413
|
|
|
3290
3414
|
// src/project/prepareSigning.ts
|
|
3291
|
-
var
|
|
3292
|
-
var
|
|
3415
|
+
var fs28 = __toESM(require("fs"), 1);
|
|
3416
|
+
var path27 = __toESM(require("path"), 1);
|
|
3293
3417
|
function hasSigningArtifacts(dir) {
|
|
3294
|
-
if (!
|
|
3295
|
-
const buildProfile =
|
|
3296
|
-
if (!
|
|
3418
|
+
if (!fs28.existsSync(path27.join(dir, "signatures"))) return false;
|
|
3419
|
+
const buildProfile = path27.join(dir, "build-profile.json5");
|
|
3420
|
+
if (!fs28.existsSync(buildProfile)) return false;
|
|
3297
3421
|
try {
|
|
3298
3422
|
const parsed = readJson5File(buildProfile);
|
|
3299
3423
|
return Array.isArray(parsed.app?.signingConfigs) && parsed.app.signingConfigs.length > 0;
|
|
@@ -3383,8 +3507,8 @@ async function diffHapAssets(opts) {
|
|
|
3383
3507
|
}
|
|
3384
3508
|
|
|
3385
3509
|
// src/install/apply.ts
|
|
3386
|
-
var
|
|
3387
|
-
var
|
|
3510
|
+
var fs29 = __toESM(require("fs"), 1);
|
|
3511
|
+
var path28 = __toESM(require("path"), 1);
|
|
3388
3512
|
function decideInstallMethod(s) {
|
|
3389
3513
|
if (!s.signInfoInconsistent) return "replace";
|
|
3390
3514
|
if (s.isSystemBundle && !s.allowUninstall) return "refuse";
|
|
@@ -3405,7 +3529,7 @@ async function pidOf(config, bundle, deviceSerial) {
|
|
|
3405
3529
|
async function resolveHapPath(opts) {
|
|
3406
3530
|
let hapPath = opts.hapPath;
|
|
3407
3531
|
if (hapPath) {
|
|
3408
|
-
if (!
|
|
3532
|
+
if (!path28.isAbsolute(hapPath) && opts.projectDir) hapPath = path28.join(opts.projectDir, hapPath);
|
|
3409
3533
|
} else {
|
|
3410
3534
|
if (!opts.projectDir) throw new OniroError("applyChanges requires hapPath or projectDir.");
|
|
3411
3535
|
const haps = await discoverHaps({ projectDir: opts.projectDir });
|
|
@@ -3424,7 +3548,7 @@ async function resolveHapPath(opts) {
|
|
|
3424
3548
|
hapPath = withSigned[0][1].signed[0];
|
|
3425
3549
|
}
|
|
3426
3550
|
}
|
|
3427
|
-
if (!
|
|
3551
|
+
if (!fs29.existsSync(hapPath)) throw new OniroError(`HAP not found at ${hapPath}.`);
|
|
3428
3552
|
return hapPath;
|
|
3429
3553
|
}
|
|
3430
3554
|
async function applyChanges(opts) {
|
|
@@ -3434,7 +3558,7 @@ async function applyChanges(opts) {
|
|
|
3434
3558
|
const allowUninstall = opts.allowUninstall ?? false;
|
|
3435
3559
|
const hapPath = await resolveHapPath(opts);
|
|
3436
3560
|
let assetsChanged = false;
|
|
3437
|
-
if (opts.installedHapPath &&
|
|
3561
|
+
if (opts.installedHapPath && fs29.existsSync(opts.installedHapPath)) {
|
|
3438
3562
|
try {
|
|
3439
3563
|
const diff = await diffHapAssets({ installedHap: opts.installedHapPath, newHap: hapPath });
|
|
3440
3564
|
assetsChanged = diff.addedAssetPaths.length > 0 || diff.removedAssetPaths.length > 0;
|
|
@@ -3476,7 +3600,7 @@ async function applyChanges(opts) {
|
|
|
3476
3600
|
}
|
|
3477
3601
|
|
|
3478
3602
|
// src/index.ts
|
|
3479
|
-
var VERSION = "0.
|
|
3603
|
+
var VERSION = "0.10.0";
|
|
3480
3604
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3481
3605
|
0 && (module.exports = {
|
|
3482
3606
|
ALL_SDKS,
|
|
@@ -3496,6 +3620,7 @@ var VERSION = "0.8.0";
|
|
|
3496
3620
|
VERSION,
|
|
3497
3621
|
applyChanges,
|
|
3498
3622
|
attemptHdcConnection,
|
|
3623
|
+
buildCmdWrapper,
|
|
3499
3624
|
buildGestureCommands,
|
|
3500
3625
|
buildGestureUitestCommand,
|
|
3501
3626
|
buildHap,
|
|
@@ -3532,6 +3657,8 @@ var VERSION = "0.8.0";
|
|
|
3532
3657
|
ensureOhModules,
|
|
3533
3658
|
ensureOk,
|
|
3534
3659
|
ensureRemoteOk,
|
|
3660
|
+
escapeCmdArgument,
|
|
3661
|
+
escapeCmdCommand,
|
|
3535
3662
|
extractTarball,
|
|
3536
3663
|
extractZipWithProgress,
|
|
3537
3664
|
findAppProcessId,
|
|
@@ -3570,12 +3697,14 @@ var VERSION = "0.8.0";
|
|
|
3570
3697
|
isRamBackedPath,
|
|
3571
3698
|
isValidBundleName,
|
|
3572
3699
|
isValidProjectName,
|
|
3700
|
+
killProcessTree,
|
|
3573
3701
|
launchApp,
|
|
3574
3702
|
listAbilities,
|
|
3575
3703
|
listDevices,
|
|
3576
3704
|
listModules,
|
|
3577
3705
|
listRunningProcesses,
|
|
3578
3706
|
listTemplates,
|
|
3707
|
+
needsCmdWrapper,
|
|
3579
3708
|
nonInteractivePrompter,
|
|
3580
3709
|
noopLogger,
|
|
3581
3710
|
noopProgress,
|
|
@@ -3612,11 +3741,13 @@ var VERSION = "0.8.0";
|
|
|
3612
3741
|
setHilogLevel,
|
|
3613
3742
|
shell,
|
|
3614
3743
|
shellChecked,
|
|
3744
|
+
spawnCompat,
|
|
3615
3745
|
startEmulator,
|
|
3616
3746
|
staticConfig,
|
|
3617
3747
|
stopEmulator,
|
|
3618
3748
|
streamHilog,
|
|
3619
3749
|
takeScreenshot,
|
|
3750
|
+
toLongPath,
|
|
3620
3751
|
toSpaceError,
|
|
3621
3752
|
uninstallApp,
|
|
3622
3753
|
validateTemplateLayout,
|