* Copyright (c) 2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import fs from 'node:fs/promises';
import path from 'node:path';
import process from 'node:process';
import { spawn } from 'node:child_process';
function binary(name) {
return process.platform === 'win32' ? `${name}.exe` : name;
}
function envPath() {
return String(process.env.DEVECO_HOME || '').trim();
}
async function isDir(file) {
if (!file) {
return false;
}
return fs.stat(file).then((info) => info.isDirectory()).catch(() => false);
}
function defaults() {
if (process.platform === 'darwin') {
return ['/Applications/DevEco-Studio.app/Contents'];
}
if (process.platform === 'linux') {
const home = String(process.env.HOME || '').trim();
return [
home ? path.join(home, 'devecostudio/Contents') : '',
home ? path.join(home, 'DevEco-Studio/Contents') : '',
].filter(Boolean);
}
const home = String(process.env.USERPROFILE || '').trim();
return [
'C:\\Program Files\\Huawei\\DevEco Studio',
'C:\\Program Files\\DevEco Studio',
'C:\\Program Files (x86)\\DevEco Studio',
home ? path.join(home, 'DevEco Studio') : '',
].filter(Boolean);
}
function nodePath(home) {
return process.platform === 'win32'
? path.join(home, 'tools', 'node', 'node.exe')
: path.join(home, 'tools', 'node', 'bin', 'node');
}
function hdcPath(home) {
return path.join(home, 'sdk', 'default', 'openharmony', 'toolchains', binary('hdc'));
}
async function exists(file) {
return fs.access(file).then(() => true).catch(() => false);
}
async function findDevEcoHome() {
const env = envPath();
if (env && (await isDir(env)) && (await exists(nodePath(env)))) {
return env;
}
for (const item of defaults()) {
if (!(await isDir(item))) {
continue;
}
if (await exists(nodePath(item))) {
return item;
}
}
}
export function targetArgs(deviceId) {
return deviceId ? ['-t', deviceId] : [];
}
export async function resolveHdcBinary() {
const home = await findDevEcoHome();
if (!home) {
return { hdc: '', home: '', error: 'DevEco Studio path not found. Set DEVECO_HOME and retry.' };
}
const hdc = hdcPath(home);
if (!(await exists(hdc))) {
return { hdc: '', home, error: `hdc not found: ${hdc}` };
}
return { hdc, home, error: '' };
}
export async function resolveHdcOrThrow() {
const resolved = await resolveHdcBinary();
if (resolved.error) {
throw new Error(resolved.error);
}
return resolved.hdc;
}
export async function runHdc(cmd) {
return new Promise((resolve) => {
const proc = spawn(cmd[0], cmd.slice(1), {
windowsHide: true,
stdio: ['ignore', 'pipe', 'pipe'],
});
let stdout = '';
let stderr = '';
proc.stdout.setEncoding('utf8');
proc.stderr.setEncoding('utf8');
proc.stdout.on('data', (chunk) => {
stdout += chunk;
});
proc.stderr.on('data', (chunk) => {
stderr += chunk;
});
proc.on('error', (error) => {
resolve({ stdout, stderr: error.message || stderr, exitCode: 1 });
});
proc.on('close', (code) => {
resolve({ stdout, stderr, exitCode: code ?? 1 });
});
});
}