234 lines
7.2 KiB
TypeScript
234 lines
7.2 KiB
TypeScript
import type {
|
||
checkIDEResultDto,
|
||
checkIDEsResultDto,
|
||
checkIDEsVersionDto,
|
||
checkIDEVersionDto,
|
||
IDECode
|
||
} from '@my-type/settings'
|
||
import { execSync } from 'node:child_process'
|
||
import path from 'path'
|
||
import { loadJsonFile } from 'load-json-file'
|
||
import os from 'os'
|
||
import {
|
||
JetBrainsIDEDisplayNameEnum as JIN,
|
||
JetBrainsProductCode,
|
||
JetBrainsStateDto,
|
||
toProductDisplayName
|
||
} from '@my-type/jetbrains-state-tools'
|
||
import { settingsManager } from '../settings'
|
||
import { isNodeError } from '@my-type/node-error'
|
||
import { JetBrainsDataProductDto } from '@my-type/jetbrains-data-products'
|
||
|
||
const JETBRAINS_TOOLBOX_STATE_JSON_PATH = path.join(
|
||
os.homedir(),
|
||
'AppData/Local/JetBrains/Toolbox/state.json'
|
||
)
|
||
|
||
/**
|
||
* 通过 where.exe 检查系统中的指定 IDE 是否安装可用。
|
||
* @param display IDE 的正式显示名称,例如 `Visual Studio Code`。
|
||
* @param command IDE 的命令行别名,例如 `code`。
|
||
* @param code (JetBrains IDE)的产品代号;对于其他 IDE 为空字符串
|
||
*/
|
||
function checkIDE(display: string, command: string, code: IDECode): checkIDEResultDto | null {
|
||
try {
|
||
const paths = execSync(`where.exe ${command}`).toString().split('\n').slice(0, -1)
|
||
return { code, display: display, command: command, paths: paths }
|
||
} catch {
|
||
return null
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 检查 JetBrains IDEs 是否安装可用。
|
||
*/
|
||
async function checkJetBrainsIDEs(): Promise<checkIDEsResultDto> {
|
||
// 构建数据结构的辅助函数
|
||
const _ = (command: string, code: JetBrainsProductCode): checkIDEResultDto => {
|
||
return {
|
||
code,
|
||
command,
|
||
display: JIN[command],
|
||
paths: []
|
||
}
|
||
}
|
||
const result: checkIDEsResultDto = {
|
||
PY: _('pycharm', 'PY'),
|
||
CL: _('clion', 'CL'),
|
||
WS: _('webstorm', 'WS'),
|
||
PS: _('phpstorm', 'PS'),
|
||
IU: _('idea', 'IU')
|
||
}
|
||
// 优先从 JBTState.json 查找
|
||
if (settingsManager._settings?.codeLaunchpadIDESearchPolicy.includes('JBTState.json')) {
|
||
// 处理文件不存在,即 JetBrains Toolbox 相关问题
|
||
try {
|
||
const data: JetBrainsStateDto = await loadJsonFile(JETBRAINS_TOOLBOX_STATE_JSON_PATH)
|
||
for (const ide of data.tools) {
|
||
for (const resultElement of Object.values(result)) {
|
||
if (resultElement?.code === ide.productCode) {
|
||
resultElement.paths = [ide.launchCommand]
|
||
}
|
||
}
|
||
}
|
||
} catch (error) {
|
||
if (isNodeError(error)) {
|
||
if (error.code === 'ENOENT') {
|
||
console.log('由于 JBTState.json 不存在,回退到 where.exe。')
|
||
}
|
||
} else {
|
||
console.error(error)
|
||
}
|
||
}
|
||
}
|
||
// 从 where.exe 查找
|
||
// 此分支确保在设置中启用 where.exe 方式,以及 JBTState.json 方式没有结果(未找到任何,或出现错误)时进入
|
||
if (
|
||
settingsManager._settings?.codeLaunchpadIDESearchPolicy.includes('where.exe') &&
|
||
Object.entries(result).length === 0
|
||
) {
|
||
result['clion'] = checkIDE(JIN.clion, 'clion', 'CL')
|
||
result['pycharm'] = checkIDE(JIN.pycharm, 'pycharm', 'PY')
|
||
result['webstorm'] = checkIDE(JIN.webstorm, 'webstorm', 'WS')
|
||
result['phpstorm'] = checkIDE(JIN.phpstorm, 'phpstorm', 'PS')
|
||
result['idea'] = checkIDE(JIN.idea, 'idea', 'IU')
|
||
}
|
||
return result
|
||
}
|
||
|
||
/**
|
||
* 获取系统中的各 IDE 是否安装可用。
|
||
*/
|
||
export async function getIDEs(): Promise<checkIDEsResultDto> {
|
||
return global.installedIDEs === null ? await checkIDEs() : global.installedIDEs
|
||
}
|
||
|
||
/**
|
||
* 检查系统中的各 IDE 是否安装可用。
|
||
* 原理是运行 `where.exe ${command}` 并检查返回值。
|
||
*/
|
||
export async function checkIDEs(): Promise<checkIDEsResultDto> {
|
||
console.log('在系统中查找已安装的 IDE ...')
|
||
const vscodeIDEs = {
|
||
VSC: checkIDE('Visual Studio Code', 'code', 'VSC')
|
||
}
|
||
global.installedIDEs = { ...vscodeIDEs, ...(await checkJetBrainsIDEs()) }
|
||
return global.installedIDEs
|
||
}
|
||
|
||
/**
|
||
* 检查 VSCode 的版本信息,通过 code --version。
|
||
*/
|
||
async function checkVSCodeVersion(): Promise<checkIDEVersionDto> {
|
||
const install = execSync('code --version').toString().split('\n')[0]
|
||
let latest = 'unknown'
|
||
try {
|
||
latest = await fetch('https://update.code.visualstudio.com/api/releases/stable')
|
||
.then((res) => res.json())
|
||
.then((data) => data[0])
|
||
} catch (error) {
|
||
console.error('获取 VSCode 版本列表时出现错误。错误提供如下。')
|
||
console.error(error)
|
||
}
|
||
return { code: 'VSC', install, latest, display: 'Visual Studio Code' }
|
||
}
|
||
|
||
/**
|
||
* 检查 JetBrains IDE 的版本信息
|
||
*/
|
||
export async function checkJetBrainsIDEsVersion(): Promise<checkIDEsVersionDto> {
|
||
// 构建数据结构的辅助函数
|
||
const _ = (code: JetBrainsProductCode): checkIDEVersionDto => {
|
||
return {
|
||
code,
|
||
display: toProductDisplayName(code) as string,
|
||
install: 'unknown',
|
||
latest: 'unknown'
|
||
}
|
||
}
|
||
const result: checkIDEsVersionDto = {
|
||
PY: _('PY'),
|
||
CL: _('CL'),
|
||
WS: _('WS'),
|
||
PS: _('PS'),
|
||
IU: _('IU')
|
||
}
|
||
|
||
// 尝试从 JBTState.json 获取已安装的 JetBrains IDEs 的版本
|
||
// TODO:添加更多获取 JetBrains IDEs 已安装版本的方案
|
||
try {
|
||
const data: JetBrainsStateDto = await loadJsonFile(JETBRAINS_TOOLBOX_STATE_JSON_PATH)
|
||
for (const tool of data.tools) {
|
||
for (const resultElement of Object.values(result)) {
|
||
if (resultElement?.code === tool.productCode) {
|
||
resultElement.install = tool.displayVersion
|
||
}
|
||
}
|
||
}
|
||
} catch (error) {
|
||
if (isNodeError(error)) {
|
||
if (error.code === 'ENOENT') {
|
||
console.log('由于 JBTState.json 不存在,检查 JetBrains IDEs 已安装版本的任务失败。')
|
||
}
|
||
} else {
|
||
console.error(error)
|
||
}
|
||
}
|
||
|
||
// 从命令行获取 JetBrains IDE 的已安装版本的方案仍然在科研中
|
||
// 所以这个方法就暂时先结束了~
|
||
|
||
// 从网络接口获取 JetBrains IDEs 的版本列表
|
||
const codes: JetBrainsProductCode[] = []
|
||
for (const resultElement of Object.values(result)) {
|
||
codes.push(<JetBrainsProductCode>resultElement?.code)
|
||
}
|
||
try {
|
||
const params = new URLSearchParams({
|
||
type: 'release',
|
||
code: codes.join(',')
|
||
})
|
||
const data: JetBrainsDataProductDto[] = await fetch(
|
||
`https://data.services.jetbrains.com/products?${params}`
|
||
).then((res) => res.json())
|
||
for (const datum of data) {
|
||
for (const resultElement of Object.values(result)) {
|
||
if (resultElement?.code === datum.intellijProductCode) {
|
||
resultElement.latest = datum.releases[0].version
|
||
}
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error('从 JetBrains IDEs 版本列表接口获取数据失败。错误提供如下。')
|
||
console.error(error)
|
||
}
|
||
|
||
return result
|
||
}
|
||
|
||
/**
|
||
* 获取各 IDE 的版本信息。
|
||
*/
|
||
export async function getIDEsVersion(): Promise<checkIDEsVersionDto> {
|
||
return global.installedIDEsVersion === null
|
||
? await checkIDEsVersion()
|
||
: global.installedIDEsVersion
|
||
}
|
||
|
||
/**
|
||
* 检查各 IDE 的版本信息。
|
||
*/
|
||
export async function checkIDEsVersion(): Promise<checkIDEsVersionDto> {
|
||
let result: checkIDEsVersionDto = {}
|
||
for (const ide in await getIDEs()) {
|
||
switch (ide) {
|
||
case 'VSC':
|
||
result['VSC'] = await checkVSCodeVersion()
|
||
break
|
||
}
|
||
}
|
||
result = { ...result, ...(await checkJetBrainsIDEsVersion()) }
|
||
global.installedIDEsVersion = result
|
||
return result
|
||
}
|