frontend/.pnpm-store/v3/files/a4/636ee7ef77be22fb79c55de334d9ea72ea8a09920369a440ee7efa2a55d23e6ff467f07ea7015bb9f97b9b769ed23372a043c303cf9c463b6fcd2605ad7e10

194 lines
5.2 KiB
Plaintext

import process from 'node:process';
import {spawn} from 'node:child_process';
import {fileURLToPath} from 'node:url';
import path from 'node:path';
import {format} from 'node:util';
import ConfigStore from 'configstore';
import chalk from 'chalk';
import semver from 'semver';
import semverDiff from 'semver-diff';
import latestVersion from 'latest-version';
import {isNpmOrYarn} from 'is-npm';
import isInstalledGlobally from 'is-installed-globally';
import isYarnGlobal from 'is-yarn-global';
import hasYarn from 'has-yarn';
import boxen from 'boxen';
import {xdgConfig} from 'xdg-basedir';
import isCi from 'is-ci';
import pupa from 'pupa';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const ONE_DAY = 1000 * 60 * 60 * 24;
export default class UpdateNotifier {
// Public
config;
update;
// Semi-private (used for tests)
_packageName;
_shouldNotifyInNpmScript;
#options;
#packageVersion;
#updateCheckInterval;
#isDisabled;
constructor(options = {}) {
this.#options = options;
options.pkg = options.pkg || {};
options.distTag = options.distTag || 'latest';
// Reduce pkg to the essential keys. with fallback to deprecated options
// TODO: Remove deprecated options at some point far into the future
options.pkg = {
name: options.pkg.name || options.packageName,
version: options.pkg.version || options.packageVersion,
};
if (!options.pkg.name || !options.pkg.version) {
throw new Error('pkg.name and pkg.version required');
}
this._packageName = options.pkg.name;
this.#packageVersion = options.pkg.version;
this.#updateCheckInterval = typeof options.updateCheckInterval === 'number' ? options.updateCheckInterval : ONE_DAY;
this.#isDisabled = 'NO_UPDATE_NOTIFIER' in process.env
|| process.env.NODE_ENV === 'test'
|| process.argv.includes('--no-update-notifier')
|| isCi;
this._shouldNotifyInNpmScript = options.shouldNotifyInNpmScript;
if (!this.#isDisabled) {
try {
this.config = new ConfigStore(`update-notifier-${this._packageName}`, {
optOut: false,
// Init with the current time so the first check is only
// after the set interval, so not to bother users right away
lastUpdateCheck: Date.now(),
});
} catch {
// Expecting error code EACCES or EPERM
const message
= chalk.yellow(format(' %s update check failed ', options.pkg.name))
+ format('\n Try running with %s or get access ', chalk.cyan('sudo'))
+ '\n to the local update config store via \n'
+ chalk.cyan(format(' sudo chown -R $USER:$(id -gn $USER) %s ', xdgConfig));
process.on('exit', () => {
console.error(boxen(message, {textAlignment: 'center'}));
});
}
}
}
check() {
if (
!this.config
|| this.config.get('optOut')
|| this.#isDisabled
) {
return;
}
this.update = this.config.get('update');
if (this.update) {
// Use the real latest version instead of the cached one
this.update.current = this.#packageVersion;
// Clear cached information
this.config.delete('update');
}
// Only check for updates on a set interval
if (Date.now() - this.config.get('lastUpdateCheck') < this.#updateCheckInterval) {
return;
}
// Spawn a detached process, passing the options as an environment property
spawn(process.execPath, [path.join(__dirname, 'check.js'), JSON.stringify(this.#options)], {
detached: true,
stdio: 'ignore',
}).unref();
}
async fetchInfo() {
const {distTag} = this.#options;
const latest = await latestVersion(this._packageName, {version: distTag});
return {
latest,
current: this.#packageVersion,
type: semverDiff(this.#packageVersion, latest) || distTag,
name: this._packageName,
};
}
notify(options) {
const suppressForNpm = !this._shouldNotifyInNpmScript && isNpmOrYarn;
if (!process.stdout.isTTY || suppressForNpm || !this.update || !semver.gt(this.update.latest, this.update.current)) {
return this;
}
options = {
isGlobal: isInstalledGlobally,
isYarnGlobal: isYarnGlobal(),
...options,
};
let installCommand;
if (options.isYarnGlobal) {
installCommand = `yarn global add ${this._packageName}`;
} else if (options.isGlobal) {
installCommand = `npm i -g ${this._packageName}`;
} else if (hasYarn()) {
installCommand = `yarn add ${this._packageName}`;
} else {
installCommand = `npm i ${this._packageName}`;
}
const defaultTemplate = 'Update available '
+ chalk.dim('{currentVersion}')
+ chalk.reset(' → ')
+ chalk.green('{latestVersion}')
+ ' \nRun ' + chalk.cyan('{updateCommand}') + ' to update';
const template = options.message || defaultTemplate;
options.boxenOptions = options.boxenOptions || {
padding: 1,
margin: 1,
textAlignment: 'center',
borderColor: 'yellow',
borderStyle: 'round',
};
const message = boxen(
pupa(template, {
packageName: this._packageName,
currentVersion: this.update.current,
latestVersion: this.update.latest,
updateCommand: installCommand,
}),
options.boxenOptions,
);
if (options.defer === false) {
console.error(message);
} else {
process.on('exit', () => {
console.error(message);
});
process.on('SIGINT', () => {
console.error('');
process.exit();
});
}
return this;
}
}