From 1f24954ddcd700cf48f1f045096e41c7d1cf6caf Mon Sep 17 00:00:00 2001 From: Shigma Date: Wed, 14 Jun 2023 23:38:50 +0800 Subject: [PATCH] feat(market): support plugin shortname and update --self --- packages/registry/src/index.ts | 14 +++-- plugins/market/src/node/index.ts | 60 ++++++++++++------- plugins/market/src/node/installer.ts | 29 +++++++-- .../market/src/node/locales/message.zh-CN.yml | 22 +++---- 4 files changed, 82 insertions(+), 43 deletions(-) diff --git a/packages/registry/src/index.ts b/packages/registry/src/index.ts index 36a3e23e..c57b7a2f 100644 --- a/packages/registry/src/index.ts +++ b/packages/registry/src/index.ts @@ -287,16 +287,20 @@ export default class Scanner { this.total = this.objects.length } + static isCompatible(range: string, remote: RemotePackage) { + const { peerDependencies = {} } = remote + const declaredVersion = peerDependencies['koishi'] + try { + return declaredVersion && intersects(range, declaredVersion) + } catch {} + } + public async process(object: SearchObject, range: string, onRegistry: AnalyzeConfig['onRegistry']) { const { name } = object.package const official = name.startsWith('@koishijs/plugin-') const registry = await this.request(`/${name}`) const compatible = Object.values(registry.versions).filter((remote) => { - const { peerDependencies = {} } = remote - const declaredVersion = peerDependencies['koishi'] - try { - return declaredVersion && intersects(range, declaredVersion) - } catch {} + return Scanner.isCompatible(range, remote) }) await onRegistry?.(registry, compatible) diff --git a/plugins/market/src/node/index.ts b/plugins/market/src/node/index.ts index def399dd..54c677c3 100644 --- a/plugins/market/src/node/index.ts +++ b/plugins/market/src/node/index.ts @@ -1,5 +1,4 @@ -import { Context, Logger, pick, Schema } from 'koishi' -import { Registry } from '@koishijs/registry' +import { Context, pick, Schema } from 'koishi' import { gt } from 'semver' import { resolve } from 'path' import Dependencies from './deps' @@ -24,8 +23,6 @@ declare module '@koishijs/plugin-console' { } } -const logger = new Logger('market') - export const name = 'market' export interface Config { @@ -50,24 +47,27 @@ export function apply(ctx: Context, config: Config) { ctx.plugin(Installer, config.registry) ctx.using(['installer'], (ctx) => { - ctx.command('plugin.install [name]', { authority: 4 }) + ctx.command('plugin.install ', { authority: 4 }) .alias('.i') .action(async ({ session }, name) => { if (!name) return session.text('.expect-name') + + // check local dependencies + const names = ctx.installer.resolveName(name) const deps = await ctx.installer.get() - if (deps[name]) return session.text('.already-installed') - let registry: Registry - try { - registry = await ctx.installer.getRegistry(name) - } catch (error) { - logger.warn(error) - return session.text('.unknown-error') - } + name = names.find((name) => deps[name]) + if (name) return session.text('.already-installed') + + // find proper version + const result = await ctx.installer.findVersion(names) + if (!result) return session.text('.not-found') + + // set restart message ctx.envData.message = { ...pick(session, ['sid', 'channelId', 'guildId', 'subtype']), content: session.text('.success'), } - await ctx.installer.install({ [name]: registry.version }) + await ctx.installer.install(result) ctx.envData.message = null return session.text('.success') }) @@ -76,28 +76,41 @@ export function apply(ctx: Context, config: Config) { .alias('.r') .action(async ({ session }, name) => { if (!name) return session.text('.expect-name') + + // check local dependencies + const names = ctx.installer.resolveName(name) const deps = await ctx.installer.get() - if (!deps[name]) return session.text('.not-installed') - ctx.envData.message = { - ...pick(session, ['sid', 'channelId', 'guildId', 'subtype']), - content: session.text('.success'), - } + name = names.find((name) => deps[name]) + if (!name) return session.text('.not-installed') + await ctx.installer.install({ [name]: null }) - ctx.envData.message = null return session.text('.success') }) - ctx.command('plugin.upgrade', { authority: 4 }) + ctx.command('plugin.upgrade [name...]', { authority: 4 }) .alias('.update', '.up') - .action(async ({ session }) => { + .option('self', '-s, --koishi') + .action(async ({ session, options }, ...names) => { + async function getPackages(names: string[]) { + if (!names.length) return Object.keys(deps) + names = names.map((name) => { + const names = ctx.installer.resolveName(name) + return names.find((name) => deps[name]) + }).filter(Boolean) + if (options.self) names.push('koishi') + return names + } + const deps = await ctx.installer.get() - const names = Object.keys(deps).filter((name) => { + names = await getPackages(names) + names = names.filter((name) => { const { latest, resolved, invalid } = deps[name] try { return !invalid && gt(latest, resolved) } catch {} }) if (!names.length) return session.text('.all-updated') + const output = names.map((name) => { const { latest, resolved } = deps[name] return `${name}: ${resolved} -> ${latest}` @@ -109,6 +122,7 @@ export function apply(ctx: Context, config: Config) { if (!['Y', 'y'].includes(result?.trim())) { return session.text('.cancelled') } + ctx.envData.message = { ...pick(session, ['sid', 'channelId', 'guildId', 'subtype']), content: session.text('.success'), diff --git a/plugins/market/src/node/installer.ts b/plugins/market/src/node/installer.ts index 46057c3e..0ae0865f 100644 --- a/plugins/market/src/node/installer.ts +++ b/plugins/market/src/node/installer.ts @@ -1,5 +1,5 @@ import { Context, defineProperty, Dict, Logger, pick, Quester, Schema, Service, Time, valueMap } from 'koishi' -import { PackageJson, Registry } from '@koishijs/registry' +import Scanner, { PackageJson, Registry } from '@koishijs/registry' import { resolve } from 'path' import { promises as fsp, readFileSync } from 'fs' import { compare, satisfies, valid } from 'semver' @@ -48,8 +48,29 @@ class Installer extends Service { this.http = this.ctx.http.extend({ endpoint: this.registry, timeout }) } - getRegistry(name: string) { - return this.http.get(`/${name}`) + resolveName(name: string) { + if (name.startsWith('@koishijs/plugin-')) return [name] + if (name.match(/(^|\/)koishi-plugin-/)) return [name] + if (name[0] === '@') { + const [left, right] = name.split('/') + return [`${left}/koishi-plugin-${right}`] + } else { + return [`@koishijs/plugin-${name}`, `koishi-plugin-${name}`] + } + } + + async findVersion(names: string[]) { + const entries = await Promise.all(names.map(async (name) => { + try { + const registry = await this.http.get(`/${name}`) + const versions = Object.values(registry.versions).filter((remote) => { + return !remote.deprecated && Scanner.isCompatible('4', remote) + }).sort((a, b) => compare(b.version, a.version)) + if (!versions.length) return + return { [name]: versions[0].version } + } catch (e) {} + })) + return entries.find(Boolean) } private async _get() { @@ -71,7 +92,7 @@ class Installer extends Service { } try { - const registry = await this.getRegistry(name) + const registry = await this.http.get(`/${name}`) const entries = Object.values(registry.versions) .map(item => [item.version, pick(item, Dependency.keys)] as const) .sort(([a], [b]) => compare(b, a)) diff --git a/plugins/market/src/node/locales/message.zh-CN.yml b/plugins/market/src/node/locales/message.zh-CN.yml index 9f255c71..605ec0b3 100644 --- a/plugins/market/src/node/locales/message.zh-CN.yml +++ b/plugins/market/src/node/locales/message.zh-CN.yml @@ -2,27 +2,27 @@ commands.plugin: description: 插件管理 commands.plugin.install: - description: 安装依赖 + description: 安装插件 messages: - expect-name: 请输入依赖名。 - already-installed: 该依赖已安装。 - unknown-error: 遇到未知错误。 + expect-name: 请输入插件名。 + already-installed: 该插件已安装。 + not-found: 未找到该插件。 success: 安装成功! commands.plugin.uninstall: - description: 卸载依赖 + description: 卸载插件 messages: - expect-name: 请输入依赖名。 - not-installed: 该依赖未安装。 - unknown-error: 遇到未知错误。 + expect-name: 请输入插件名。 + not-installed: 该插件未安装。 success: 卸载成功! commands.plugin.upgrade: - description: 升级依赖 + description: 升级插件 + options: + self: 升级 Koishi 本体 messages: - all-updated: 所有依赖已是最新版本。 + all-updated: 所有插件已是最新版本。 available: 有可用的依赖更新: prompt: 输入「Y」升级全部依赖,输入「N」取消操作。 cancelled: 已取消操作。 - unknown-error: 遇到未知错误。 success: 升级成功!