mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-05-16 18:08:41 +00:00
197 lines
5.5 KiB
TypeScript
197 lines
5.5 KiB
TypeScript
import type { MessageService } from '@theia/core/lib/common/message-service';
|
|
import { nls } from '@theia/core/lib/common/nls';
|
|
import {
|
|
coerce as coerceSemver,
|
|
compare as compareSemver,
|
|
parse as parseSemver,
|
|
} from 'semver';
|
|
import { naturalCompare } from '../utils';
|
|
import type { ArduinoComponent } from './arduino-component';
|
|
import { ExecuteWithProgress } from './progressible';
|
|
import type { ResponseServiceClient } from './response-service';
|
|
|
|
export function libraryInstallFailed(
|
|
name: string,
|
|
version?: string | undefined
|
|
): string {
|
|
const versionSuffix = version ? `:${version}` : '';
|
|
return nls.localize(
|
|
'arduino/installable/libraryInstallFailed',
|
|
"Failed to install library: '{0}{1}'.",
|
|
name,
|
|
versionSuffix
|
|
);
|
|
}
|
|
|
|
export function platformInstallFailed(
|
|
name: string,
|
|
version?: string | undefined
|
|
): string {
|
|
const versionSuffix = version ? `:${version}` : '';
|
|
return nls.localize(
|
|
'arduino/installable/platformInstallFailed',
|
|
"Failed to install platform: '{0}{1}'.",
|
|
name,
|
|
versionSuffix
|
|
);
|
|
}
|
|
|
|
export interface Installable<T extends ArduinoComponent> {
|
|
/**
|
|
* If `options.version` is specified, that will be installed. Otherwise, `item.availableVersions[0]`.
|
|
*/
|
|
install(options: {
|
|
item: T;
|
|
progressId?: string;
|
|
version?: Installable.Version;
|
|
noOverwrite?: boolean;
|
|
}): Promise<void>;
|
|
|
|
/**
|
|
* Uninstalls the given component. It is a NOOP if not installed.
|
|
*/
|
|
uninstall(options: { item: T; progressId?: string }): Promise<void>;
|
|
}
|
|
export namespace Installable {
|
|
export type Version = string;
|
|
|
|
export namespace Version {
|
|
/**
|
|
* Most recent version comes first, then the previous versions. (`1.8.1`, `1.6.3`, `1.6.2`, `1.6.1` and so on.)
|
|
*
|
|
* If `coerce` is `true` tries to convert any invalid semver strings to a valid semver based on [these](https://github.com/npm/node-semver#coercion) rules.
|
|
*/
|
|
export const COMPARATOR = (
|
|
left: Version,
|
|
right: Version,
|
|
coerce = false
|
|
): number => {
|
|
const validLeft = parseSemver(left);
|
|
const validRight = parseSemver(right);
|
|
if (validLeft && validRight) {
|
|
return compareSemver(validLeft, validRight);
|
|
}
|
|
if (coerce) {
|
|
const coercedLeft = validLeft ?? coerceSemver(left);
|
|
const coercedRight = validRight ?? coerceSemver(right);
|
|
if (coercedLeft && coercedRight) {
|
|
return compareSemver(coercedLeft, coercedRight);
|
|
}
|
|
}
|
|
return naturalCompare(left, right);
|
|
};
|
|
}
|
|
|
|
export const ActionLiterals = [
|
|
'installLatest',
|
|
'installSelected',
|
|
'update',
|
|
'remove',
|
|
'unknown',
|
|
] as const;
|
|
export type Action = (typeof ActionLiterals)[number];
|
|
|
|
export function action(params: {
|
|
installed?: Version | undefined;
|
|
available: Version[];
|
|
selected?: Version;
|
|
}): Action {
|
|
const { installed, available } = params;
|
|
const latest = Installable.latest(available);
|
|
if (!latest || (installed && !available.includes(installed))) {
|
|
return 'unknown';
|
|
}
|
|
const selected = params.selected ?? latest;
|
|
if (installed === selected) {
|
|
return 'remove';
|
|
}
|
|
if (installed) {
|
|
return selected === latest && installed !== latest
|
|
? 'update'
|
|
: 'installSelected';
|
|
} else {
|
|
return selected === latest ? 'installLatest' : 'installSelected';
|
|
}
|
|
}
|
|
|
|
export function latest(versions: Version[]): Version | undefined {
|
|
if (!versions.length) {
|
|
return undefined;
|
|
}
|
|
const ordered = versions.slice().sort(Installable.Version.COMPARATOR);
|
|
return ordered[ordered.length - 1];
|
|
}
|
|
|
|
export const Installed = <T extends ArduinoComponent>({
|
|
installedVersion,
|
|
}: T): boolean => {
|
|
return !!installedVersion;
|
|
};
|
|
|
|
export const Updateable = <T extends ArduinoComponent>(item: T): boolean => {
|
|
const { installedVersion } = item;
|
|
if (!installedVersion) {
|
|
return false;
|
|
}
|
|
const latestVersion = item.availableVersions[0];
|
|
if (!latestVersion) {
|
|
console.warn(
|
|
`Installed version ${installedVersion} is available for ${item.name}, but no available versions were available. Skipping.`
|
|
);
|
|
return false;
|
|
}
|
|
const result = Installable.Version.COMPARATOR(
|
|
latestVersion,
|
|
installedVersion,
|
|
true
|
|
);
|
|
return result > 0;
|
|
};
|
|
|
|
export async function installWithProgress<
|
|
T extends ArduinoComponent
|
|
>(options: {
|
|
installable: Installable<T>;
|
|
messageService: MessageService;
|
|
responseService: ResponseServiceClient;
|
|
item: T;
|
|
version: Installable.Version;
|
|
keepOutput?: boolean;
|
|
}): Promise<void> {
|
|
const { item, version } = options;
|
|
return ExecuteWithProgress.doWithProgress({
|
|
...options,
|
|
progressText: `Processing ${item.name}:${version}`,
|
|
run: ({ progressId }) =>
|
|
options.installable.install({
|
|
item: options.item,
|
|
version: options.version,
|
|
progressId,
|
|
}),
|
|
});
|
|
}
|
|
|
|
export async function uninstallWithProgress<
|
|
T extends ArduinoComponent
|
|
>(options: {
|
|
installable: Installable<T>;
|
|
messageService: MessageService;
|
|
responseService: ResponseServiceClient;
|
|
item: T;
|
|
keepOutput?: boolean;
|
|
}): Promise<void> {
|
|
const { item } = options;
|
|
return ExecuteWithProgress.doWithProgress({
|
|
...options,
|
|
progressText: `Processing ${item.name}${
|
|
item.installedVersion ? `:${item.installedVersion}` : ''
|
|
}`,
|
|
run: ({ progressId }) =>
|
|
options.installable.uninstall({
|
|
item: options.item,
|
|
progressId,
|
|
}),
|
|
});
|
|
}
|
|
}
|