Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[pull] main from yeoman:main #3

Open
wants to merge 27 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .github/funding.yml

This file was deleted.

21 changes: 21 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: CI
on:
- push
- pull_request
jobs:
test:
name: Node.js ${{ matrix.node-version }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
node-version:
- 20
- 18
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm install
- run: npm test
5 changes: 0 additions & 5 deletions .travis.yml

This file was deleted.

14 changes: 6 additions & 8 deletions check.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
/* eslint-disable unicorn/no-process-exit */
'use strict';
let updateNotifier = require('.');
import process from 'node:process';
import UpdateNotifier from './update-notifier.js';

const options = JSON.parse(process.argv[2]);
const updateNotifier = new UpdateNotifier(JSON.parse(process.argv[2]));

updateNotifier = new updateNotifier.UpdateNotifier(options);

(async () => {
try {
// Exit process when offline
setTimeout(process.exit, 1000 * 30);

Expand All @@ -22,7 +20,7 @@ updateNotifier = new updateNotifier.UpdateNotifier(options);
// Call process exit explicitly to terminate the child process,
// otherwise the child process will run forever, according to the Node.js docs
process.exit();
})().catch(error => {
} catch (error) {
console.error(error);
process.exit(1);
});
}
1 change: 0 additions & 1 deletion contributing.md

This file was deleted.

10 changes: 5 additions & 5 deletions example.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
'use strict';
const updateNotifier = require('.');
import updateNotifier from './index.js';

// Run: $ node example

Expand All @@ -10,7 +9,8 @@ const updateNotifier = require('.');
updateNotifier({
pkg: {
name: 'public-ip',
version: '0.9.2'
version: '0.9.2',
},
updateCheckInterval: 0
}).notify();
updateCheckInterval: 0,
})
.notify();
187 changes: 3 additions & 184 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,188 +1,7 @@
'use strict';
const {spawn} = require('child_process');
const path = require('path');
const {format} = require('util');
const importLazy = require('import-lazy')(require);
import UpdateNotifier from './update-notifier.js';

const configstore = importLazy('configstore');
const chalk = importLazy('chalk');
const semver = importLazy('semver');
const semverDiff = importLazy('semver-diff');
const latestVersion = importLazy('latest-version');
const isNpm = importLazy('is-npm');
const isInstalledGlobally = importLazy('is-installed-globally');
const isYarnGlobal = importLazy('is-yarn-global');
const hasYarn = importLazy('has-yarn');
const boxen = importLazy('boxen');
const xdgBasedir = importLazy('xdg-basedir');
const isCi = importLazy('is-ci');
const pupa = importLazy('pupa');

const ONE_DAY = 1000 * 60 * 60 * 24;

class UpdateNotifier {
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.disabled = 'NO_UPDATE_NOTIFIER' in process.env ||
process.env.NODE_ENV === 'test' ||
process.argv.includes('--no-update-notifier') ||
isCi();
this.shouldNotifyInNpmScript = options.shouldNotifyInNpmScript;

if (!this.disabled) {
try {
const ConfigStore = configstore();
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 ', xdgBasedir().config));

process.on('exit', () => {
console.error(boxen()(message, {align: 'center'}));
});
}
}
}

check() {
if (
!this.config ||
this.config.get('optOut') ||
this.disabled
) {
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 && isNpm().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,
align: '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;
}
}

module.exports = options => {
export default function updateNotifier(options) {
const updateNotifier = new UpdateNotifier(options);
updateNotifier.check();
return updateNotifier;
};

module.exports.UpdateNotifier = UpdateNotifier;
}
52 changes: 28 additions & 24 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,23 +1,27 @@
{
"name": "update-notifier",
"version": "5.0.1",
"version": "7.3.1",
"description": "Update notifications for your CLI app",
"license": "BSD-2-Clause",
"repository": "yeoman/update-notifier",
"funding": "https://github.com/yeoman/update-notifier?sponsor=1",
"repository": "sindresorhus/update-notifier",
"funding": "https://github.com/sponsors/sindresorhus",
"author": {
"name": "Sindre Sorhus",
"email": "[email protected]",
"url": "https://sindresorhus.com"
},
"type": "module",
"exports": "./index.js",
"sideEffects": false,
"engines": {
"node": ">=10"
"node": ">=18"
},
"scripts": {
"test": "xo && ava --timeout=20s --serial"
"test": "xo && NODE_OPTIONS='--loader=esmock --no-warnings' ava"
},
"files": [
"index.js",
"update-notifier.js",
"check.js"
],
"keywords": [
Expand All @@ -34,27 +38,27 @@
"version"
],
"dependencies": {
"boxen": "^4.2.0",
"chalk": "^4.1.0",
"configstore": "^5.0.1",
"has-yarn": "^2.1.0",
"import-lazy": "^2.1.0",
"is-ci": "^2.0.0",
"is-installed-globally": "^0.3.2",
"is-npm": "^5.0.0",
"is-yarn-global": "^0.3.0",
"latest-version": "^5.1.0",
"pupa": "^2.1.1",
"semver": "^7.3.2",
"semver-diff": "^3.1.1",
"xdg-basedir": "^4.0.0"
"boxen": "^8.0.1",
"chalk": "^5.3.0",
"configstore": "^7.0.0",
"is-in-ci": "^1.0.0",
"is-installed-globally": "^1.0.0",
"is-npm": "^6.0.0",
"latest-version": "^9.0.0",
"pupa": "^3.1.0",
"semver": "^7.6.3",
"xdg-basedir": "^5.1.0"
},
"devDependencies": {
"ava": "^2.4.0",
"clear-module": "^4.1.1",
"ava": "^6.1.3",
"clear-module": "^4.1.2",
"esmock": "^2.6.7",
"fixture-stdout": "^0.2.1",
"mock-require": "^3.0.3",
"strip-ansi": "^6.0.0",
"xo": "^0.34.1"
"strip-ansi": "^7.1.0",
"xo": "^0.59.3"
},
"ava": {
"timeout": "20s",
"serial": true
}
}
Loading