{"version":3,"file":"command-exists.mjs","names":["constants"],"sources":["../src/command-exists.ts"],"sourcesContent":["/* -------------------------------------------------------------------\n\n                       ⚡ Storm Software - Stryke\n\n This code was released as part of the Stryke project. Stryke\n is maintained by Storm Software under the Apache-2.0 license, and is\n free for commercial and private use. For more information, please visit\n our licensing page at https://stormsoftware.com/licenses/projects/stryke.\n\n Website:                  https://stormsoftware.com\n Repository:               https://github.com/storm-software/stryke\n Documentation:            https://docs.stormsoftware.com/projects/stryke\n Contact:                  https://stormsoftware.com/contact\n\n SPDX-License-Identifier:  Apache-2.0\n\n ------------------------------------------------------------------- */\n\nimport { exec, execSync } from \"node:child_process\";\nimport { accessSync } from \"node:fs\";\nimport { access, constants } from \"node:fs/promises\";\nimport { basename, dirname } from \"node:path\";\n\nconst isUsingWindows = process.platform == \"win32\";\n\nasync function fileNotExists(commandName: string) {\n  try {\n    await access(commandName, constants.F_OK);\n    return false;\n    // eslint-disable-next-line unused-imports/no-unused-vars, ts/no-unused-vars\n  } catch (_) {\n    return true;\n  }\n}\n\nfunction fileNotExistsSync(commandName: string) {\n  try {\n    accessSync(commandName, constants.F_OK);\n    return false;\n    // eslint-disable-next-line unused-imports/no-unused-vars, ts/no-unused-vars\n  } catch (_) {\n    return true;\n  }\n}\n\nasync function localExecutable(commandName: string) {\n  return access(commandName, constants.F_OK | constants.X_OK);\n}\n\nfunction localExecutableSync(commandName: string) {\n  try {\n    accessSync(commandName, constants.F_OK | constants.X_OK);\n    return true;\n    // eslint-disable-next-line unused-imports/no-unused-vars, ts/no-unused-vars\n  } catch (_) {\n    return false;\n  }\n}\n\nasync function commandExistsUnix(\n  commandName: string,\n  cleanedCommandName: string\n) {\n  const isFile = await fileNotExists(commandName);\n\n  if (!isFile) {\n    exec(\n      `command -v ${cleanedCommandName} 2>/dev/null` +\n        ` && { echo >&1 ${cleanedCommandName}; exit 0; }`\n    );\n\n    await localExecutable(commandName);\n  }\n}\n\nasync function commandExistsWindows(\n  commandName: string,\n  cleanedCommandName: string\n) {\n  // Regex from Julio from: https://stackoverflow.com/questions/51494579/regex-windows-path-validator\n  if (!/^(?!(?:.*\\s|.*\\.|\\W+)$)(?:[a-z]:)?[^<>:\"|?*\\n]+$/im.test(commandName)) {\n    return;\n  }\n\n  exec(`where ${cleanedCommandName}`);\n}\n\nfunction commandExistsUnixSync(\n  commandName: string,\n  cleanedCommandName: string\n): boolean {\n  if (fileNotExistsSync(commandName)) {\n    try {\n      const stdout = execSync(\n        `command -v ${cleanedCommandName} 2>/dev/null` +\n          ` && { echo >&1 ${cleanedCommandName}; exit 0; }`\n      );\n\n      return !!stdout;\n      // eslint-disable-next-line unused-imports/no-unused-vars, ts/no-unused-vars\n    } catch (_) {\n      return false;\n    }\n  }\n  return localExecutableSync(commandName);\n}\n\nfunction commandExistsWindowsSync(\n  commandName: string,\n  cleanedCommandName: string\n) {\n  // Regex from Julio from: https://stackoverflow.com/questions/51494579/regex-windows-path-validator\n  if (!/^(?!(?:.*\\s|.*\\.|\\W+)$)(?:[a-z]:)?[^<>:\"|?*\\n]+$/im.test(commandName)) {\n    return false;\n  }\n  try {\n    const stdout = execSync(`where ${cleanedCommandName}`, { stdio: [] });\n\n    return !!stdout;\n    // eslint-disable-next-line unused-imports/no-unused-vars, ts/no-unused-vars\n  } catch (_) {\n    return false;\n  }\n}\n\nfunction cleanInput(s: string) {\n  if (/[^\\w/:=-]/.test(s)) {\n    s = `'${s.replace(/'/g, \"'\\\\''\")}'`;\n    s = s\n      .replace(/^(?:'')+/g, \"\") // un-duplicate single-quote at the beginning\n      .replace(/\\\\'''/g, \"\\\\'\"); // remove non-escaped single-quote if there are enclosed between 2 escaped\n  }\n  return s;\n}\n\nconst cleanWindowsInput = (s: string) => {\n  const isPathName = /\\\\/.test(s);\n  if (isPathName) {\n    return `\"${dirname(s)}:${basename(s)}\"`;\n  }\n  return `\"${s}\"`;\n};\n\n/**\n * Asynchronously checks if a command exists in the system.\n *\n * @remarks\n * This function will check if the command is available in the system's PATH and if it is executable.\n * @param commandName - The name of the command to check for existence\n * @returns A promise that resolves to `true` if the command exists and is executable, `false` otherwise\n */\nexport async function commandExists(commandName: string) {\n  const cleanedCommandName = cleanInput(commandName);\n\n  if (typeof Promise !== \"undefined\") {\n    return commandExists(commandName);\n  }\n  if (isUsingWindows) {\n    return commandExistsWindows(commandName, cleanedCommandName);\n  } else {\n    return commandExistsUnix(commandName, cleanedCommandName);\n  }\n}\n\n/**\n * Synchronously checks if a command exists in the system.\n *\n * @remarks\n * This function will check if the command is available in the system's PATH and if it is executable.\n * @param commandName - The name of the command to check for existence\n * @returns `true` if the command exists and is executable, `false` otherwise\n */\nexport function commandExistsSync(commandName: string): boolean {\n  if (isUsingWindows) {\n    return commandExistsWindowsSync(\n      commandName,\n      cleanWindowsInput(commandName)\n    );\n  } else {\n    return commandExistsUnixSync(commandName, cleanInput(commandName));\n  }\n}\n"],"mappings":";;;;;;AAuBA,MAAM,iBAAiB,QAAQ,YAAY;AAE3C,eAAe,cAAc,aAAqB;AAChD,KAAI;AACF,QAAM,OAAO,aAAaA,YAAU,KAAK;AACzC,SAAO;UAEA,GAAG;AACV,SAAO;;;AAIX,SAAS,kBAAkB,aAAqB;AAC9C,KAAI;AACF,aAAW,aAAaA,YAAU,KAAK;AACvC,SAAO;UAEA,GAAG;AACV,SAAO;;;AAIX,eAAe,gBAAgB,aAAqB;AAClD,QAAO,OAAO,aAAaA,YAAU,OAAOA,YAAU,KAAK;;AAG7D,SAAS,oBAAoB,aAAqB;AAChD,KAAI;AACF,aAAW,aAAaA,YAAU,OAAOA,YAAU,KAAK;AACxD,SAAO;UAEA,GAAG;AACV,SAAO;;;AAIX,eAAe,kBACb,aACA,oBACA;AAGA,KAAI,CAAC,MAFgB,cAAc,YAAY,EAElC;AACX,OACE,cAAc,mBAAmB,6BACb,mBAAmB,aACxC;AAED,QAAM,gBAAgB,YAAY;;;AAItC,eAAe,qBACb,aACA,oBACA;AAEA,KAAI,CAAC,qDAAqD,KAAK,YAAY,CACzE;AAGF,MAAK,SAAS,qBAAqB;;AAGrC,SAAS,sBACP,aACA,oBACS;AACT,KAAI,kBAAkB,YAAY,CAChC,KAAI;AAMF,SAAO,CAAC,CALO,SACb,cAAc,mBAAmB,6BACb,mBAAmB,aAG1B;UAER,GAAG;AACV,SAAO;;AAGX,QAAO,oBAAoB,YAAY;;AAGzC,SAAS,yBACP,aACA,oBACA;AAEA,KAAI,CAAC,qDAAqD,KAAK,YAAY,CACzE,QAAO;AAET,KAAI;AAGF,SAAO,CAAC,CAFO,SAAS,SAAS,sBAAsB,EAAE,OAAO,EAAE,EAAE,CAErD;UAER,GAAG;AACV,SAAO;;;AAIX,SAAS,WAAW,GAAW;AAC7B,KAAI,YAAY,KAAK,EAAE,EAAE;AACvB,MAAI,IAAI,EAAE,QAAQ,MAAM,QAAQ,CAAC;AACjC,MAAI,EACD,QAAQ,aAAa,GAAG,CACxB,QAAQ,UAAU,MAAM;;AAE7B,QAAO;;AAGT,MAAM,qBAAqB,MAAc;AAEvC,KADmB,KAAK,KAAK,EACf,CACZ,QAAO,IAAI,QAAQ,EAAE,CAAC,GAAG,SAAS,EAAE,CAAC;AAEvC,QAAO,IAAI,EAAE;;;;;;;;;;AAWf,eAAsB,cAAc,aAAqB;CACvD,MAAM,qBAAqB,WAAW,YAAY;AAElD,KAAI,OAAO,YAAY,YACrB,QAAO,cAAc,YAAY;AAEnC,KAAI,eACF,QAAO,qBAAqB,aAAa,mBAAmB;KAE5D,QAAO,kBAAkB,aAAa,mBAAmB;;;;;;;;;;AAY7D,SAAgB,kBAAkB,aAA8B;AAC9D,KAAI,eACF,QAAO,yBACL,aACA,kBAAkB,YAAY,CAC/B;KAED,QAAO,sBAAsB,aAAa,WAAW,YAAY,CAAC"}