killOwnTree.mjs

import { execSync } from 'child_process'
import isChildAlive from './isChildAlive.mjs'


/**
 * 同步殺自建子行程之整棵行程樹;已結束或無PID者不下殺(其PID可能已被系統回收給無關行程)
 *
 * Windows以同步`taskkill /F /T /PID <pid>`;其他平台先殺行程群組,失敗再退回child.kill('SIGKILL')。
 * 同步:exit/SIGINT/SIGTERM處理器內非同步spawn不會被等待。是否真的結束由呼叫端回驗(waitChildExit或pidExists)。
 * 移植自w-web-task test/tools/harnessProc.mjs
 *
 * @param {ChildProcess} child 輸入自建子行程
 * @param {Object} [opt={}] 輸入設定物件,預設{}
 * @param {Function} [opt.exec=execSync] 輸入同步執行指令函數,預設execSync
 * @param {String} [opt.platform=process.platform] 輸入作業系統字串,預設process.platform
 * @param {Function} [opt.killGroup] 輸入殺行程群組函數,預設process.kill(-pid,'SIGKILL')
 * @returns {Boolean} 回傳是否有下殺
 * @example
 *
 * import killOwnTree from 'w-package-tools-e2e/src/killOwnTree.mjs'
 *
 * killOwnTree(backendChild)
 *
 */
function killOwnTree(child, opt = {}) {
    let { exec = execSync, platform = process.platform, killGroup = (pid) => process.kill(-pid, 'SIGKILL') } = opt
    if (!child || !child.pid || !isChildAlive(child)) return false
    if (platform === 'win32') {
        try {
            exec(`taskkill /F /T /PID ${child.pid}`, { stdio: 'ignore', timeout: 10000, windowsHide: true })
        }
        catch (e) {} //失敗與否一律由呼叫端回驗
        return true
    }
    try {
        killGroup(child.pid)
    }
    catch (e) {
        try {
            child.kill('SIGKILL')
        }
        catch (e2) {}
    }
    return true
}


export default killOwnTree