170 lines
4.3 KiB
TypeScript
170 lines
4.3 KiB
TypeScript
|
|
import * as plugins from './smartupdate.plugins.js';
|
||
|
|
import {
|
||
|
|
LOG_LEVELS,
|
||
|
|
type TLogLevel,
|
||
|
|
DEFAULT_MESSAGE_COLOR,
|
||
|
|
MESSAGE_PREFIXES,
|
||
|
|
} from './smartupdate.constants.js';
|
||
|
|
import type { INotificationOptions, IUpdateCheckResult } from './smartupdate.interfaces.js';
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Handles all user-facing notifications and console output
|
||
|
|
*/
|
||
|
|
export class UpdateNotifier {
|
||
|
|
private logLevel: TLogLevel;
|
||
|
|
private useColors: boolean;
|
||
|
|
private customLogger?: (level: TLogLevel, message: string) => void;
|
||
|
|
|
||
|
|
constructor(options: INotificationOptions) {
|
||
|
|
this.logLevel = options.logLevel;
|
||
|
|
this.useColors = options.useColors && !process.env.NO_COLOR;
|
||
|
|
this.customLogger = options.customLogger;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Check if a message at the given level should be logged
|
||
|
|
*/
|
||
|
|
private shouldLog(level: TLogLevel): boolean {
|
||
|
|
return LOG_LEVELS[level] <= LOG_LEVELS[this.logLevel];
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Colorize a string if colors are enabled
|
||
|
|
*/
|
||
|
|
private colorize(text: string, color: any = DEFAULT_MESSAGE_COLOR): string {
|
||
|
|
if (!this.useColors) {
|
||
|
|
return text;
|
||
|
|
}
|
||
|
|
return plugins.consolecolor.coloredString(text, color as any);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Log a message at the specified level
|
||
|
|
*/
|
||
|
|
private log(level: TLogLevel, message: string): void {
|
||
|
|
if (!this.shouldLog(level)) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (this.customLogger) {
|
||
|
|
this.customLogger(level, message);
|
||
|
|
} else {
|
||
|
|
console.log(message);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Log a debug message
|
||
|
|
*/
|
||
|
|
public debug(message: string): void {
|
||
|
|
this.log('DEBUG', `${MESSAGE_PREFIXES.INFO} ${message}`);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Log an info message
|
||
|
|
*/
|
||
|
|
public info(message: string): void {
|
||
|
|
this.log('INFO', `${MESSAGE_PREFIXES.SMARTUPDATE} ${message}`);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Log a warning message
|
||
|
|
*/
|
||
|
|
public warn(message: string): void {
|
||
|
|
this.log('WARN', `${MESSAGE_PREFIXES.WARN} ${message}`);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Log an error message
|
||
|
|
*/
|
||
|
|
public error(message: string): void {
|
||
|
|
this.log('ERROR', `${MESSAGE_PREFIXES.ERROR} ${message}`);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Notify about checking for updates
|
||
|
|
*/
|
||
|
|
public notifyCheckingForUpdate(packageName: string): void {
|
||
|
|
this.info(
|
||
|
|
`checking for newer version of ${this.colorize(packageName)}...`
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Notify that the package is up to date
|
||
|
|
*/
|
||
|
|
public notifyUpToDate(packageName: string): void {
|
||
|
|
this.info(
|
||
|
|
`You are running the latest version of ${this.colorize(packageName)}`
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Notify that an update is available
|
||
|
|
*/
|
||
|
|
public notifyUpdateAvailable(packageName: string, currentVersion: string, latestVersion: string): void {
|
||
|
|
this.warn(`There is a newer version of ${packageName} available on npm.`);
|
||
|
|
this.warn(`Your version: ${currentVersion} | version on npm: ${latestVersion}`);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Notify that a check was skipped due to rate limiting
|
||
|
|
*/
|
||
|
|
public notifyCheckSkipped(packageName: string, nextCheckMinutes: number): void {
|
||
|
|
const minutes = Math.floor(nextCheckMinutes) + 1;
|
||
|
|
this.info(
|
||
|
|
`Already checked recently. Next check available in ${minutes} minute${minutes !== 1 ? 's' : ''}: ` +
|
||
|
|
this.colorize(packageName)
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Notify that the changelog is being opened
|
||
|
|
*/
|
||
|
|
public notifyOpeningChangelog(): void {
|
||
|
|
this.info('Opening changelog in browser...');
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Notify about a registry error
|
||
|
|
*/
|
||
|
|
public notifyRegistryError(): void {
|
||
|
|
this.warn('Failed to retrieve package information.');
|
||
|
|
this.info('Is the registry down?');
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Notify with a complete update check result
|
||
|
|
*/
|
||
|
|
public notifyUpdateCheckResult(result: IUpdateCheckResult): void {
|
||
|
|
switch (result.status) {
|
||
|
|
case 'up-to-date':
|
||
|
|
this.notifyUpToDate(result.packageName);
|
||
|
|
break;
|
||
|
|
|
||
|
|
case 'update-available':
|
||
|
|
if (result.latestVersion) {
|
||
|
|
this.notifyUpdateAvailable(
|
||
|
|
result.packageName,
|
||
|
|
result.currentVersion,
|
||
|
|
result.latestVersion
|
||
|
|
);
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
|
||
|
|
case 'check-skipped':
|
||
|
|
if (result.nextCheckTime && result.reason) {
|
||
|
|
const minutesUntilNext = (result.nextCheckTime.getTime() - result.checkTime.getTime()) / 60000;
|
||
|
|
this.notifyCheckSkipped(result.packageName, minutesUntilNext);
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
|
||
|
|
case 'error':
|
||
|
|
if (result.error) {
|
||
|
|
this.error(result.error.message);
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|