Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: sindresorhus/update-notifier
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 5cd65771e2fab2567592ae217d3ce647679462af
Choose a base ref
...
head repository: sindresorhus/update-notifier
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: a7bb3ee3a6e3c8550fb1f2ba132411f4526844c3
Choose a head ref
  • 10 commits
  • 9 files changed
  • 5 contributors

Commits on May 13, 2018

  1. Docs: isGlobal option does not default to true (#142)

    The value has been auto-detected since #114
    dideler authored and SBoudrias committed May 13, 2018
    Copy the full SHA
    d371834 View commit details

Commits on Sep 12, 2018

  1. Copy the full SHA
    8df01b3 View commit details

Commits on Mar 28, 2019

  1. Copy the full SHA
    0d49f51 View commit details
  2. Require Node.js 8

    sindresorhus committed Mar 28, 2019
    Copy the full SHA
    aafd8a0 View commit details

Commits on Apr 7, 2019

  1. Add failing test for #153 (#154)

    LitoMore authored and sindresorhus committed Apr 7, 2019
    Copy the full SHA
    14632e4 View commit details
  2. Add distTag option (#151)

    LitoMore authored and sindresorhus committed Apr 7, 2019
    Copy the full SHA
    c8faa84 View commit details

Commits on Apr 12, 2019

  1. Fix failing test (#155)

    LitoMore authored and SBoudrias committed Apr 12, 2019
    Copy the full SHA
    79e89ad View commit details

Commits on Apr 19, 2019

  1. Copy the full SHA
    5f06620 View commit details

Commits on May 10, 2019

  1. Copy the full SHA
    ad8ed1b View commit details
  2. 3.0.0

    SBoudrias committed May 10, 2019
    Copy the full SHA
    a7bb3ee View commit details
Showing with 174 additions and 109 deletions.
  1. +1 −2 .gitattributes
  2. +2 −2 .travis.yml
  3. +10 −4 check.js
  4. +54 −28 index.js
  5. +55 −53 package.json
  6. +37 −9 readme.md
  7. +2 −3 test/fs-error.js
  8. +4 −4 test/notify.js
  9. +9 −4 test/update-notifier.js
3 changes: 1 addition & 2 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
* text=auto
*.js text eol=lf
* text=auto eol=lf
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
language: node_js
node_js:
- '6'
- '4'
- '10'
- '8'
14 changes: 10 additions & 4 deletions check.js
Original file line number Diff line number Diff line change
@@ -6,17 +6,23 @@ const options = JSON.parse(process.argv[2]);

updateNotifier = new updateNotifier.UpdateNotifier(options);

updateNotifier.checkNpm().then(update => {
(async () => {
// Exit process when offline
setTimeout(process.exit, 1000 * 30);

const update = await updateNotifier.checkNpm();

// Only update the last update check time on success
updateNotifier.config.set('lastUpdateCheck', Date.now());

if (update.type && update.type !== 'latest') {
updateNotifier.config.set('update', update);
}

// Call process exit explicitly to terminate the child process
// Otherwise the child process will run forever, according to the Node.js docs
// 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(() => {
})().catch(error => {
console.error(error);
process.exit(1);
});
82 changes: 54 additions & 28 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use strict';
const spawn = require('child_process').spawn;
const {spawn} = require('child_process');
const path = require('path');
const format = require('util').format;
const {format} = require('util');
const importLazy = require('import-lazy')(require);

const configstore = importLazy('configstore');
@@ -10,16 +10,19 @@ 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 ONE_DAY = 1000 * 60 * 60 * 24;

class UpdateNotifier {
constructor(options) {
options = options || {};
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
@@ -38,7 +41,7 @@ class UpdateNotifier {
this.hasCallback = typeof options.callback === 'function';
this.callback = options.callback || (() => {});
this.disabled = 'NO_UPDATE_NOTIFIER' in process.env ||
process.argv.indexOf('--no-update-notifier') !== -1 ||
process.argv.includes('--no-update-notifier') ||
isCi();
this.shouldNotifyInNpmScript = options.shouldNotifyInNpmScript;

@@ -51,25 +54,31 @@ class UpdateNotifier {
// after the set interval, so not to bother users right away
lastUpdateCheck: Date.now()
});
} catch (err) {
} catch (error) {
// Expecting error code EACCES or EPERM
const msg =
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('\n' + boxen()(msg, {align: 'center'}));
console.error('\n' + boxen()(message, {align: 'center'}));
});
}
}
}

check() {
if (this.hasCallback) {
this.checkNpm()
.then(update => this.callback(null, update))
.catch(err => this.callback(err));
(async () => {
try {
this.callback(null, await this.checkNpm());
} catch (error) {
this.callback(error);
}
})();

return;
}

@@ -98,38 +107,55 @@ class UpdateNotifier {
stdio: 'ignore'
}).unref();
}
checkNpm() {
return latestVersion()(this.packageName).then(latestVersion => {
return {
latest: latestVersion,
current: this.packageVersion,
type: semverDiff()(this.packageVersion, latestVersion) || 'latest',
name: this.packageName
};
});

async checkNpm() {
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(opts) {
const suppressForNpm = !this.shouldNotifyInNpmScript && isNpm();

notify(options) {
const suppressForNpm = !this.shouldNotifyInNpmScript && isNpm().isNpm;
if (!process.stdout.isTTY || suppressForNpm || !this.update) {
return this;
}

opts = Object.assign({isGlobal: isInstalledGlobally()}, opts);
options = {
isGlobal: isInstalledGlobally(),
isYarnGlobal: isYarnGlobal()(),
...options
};

let installCommand;

if (options.isYarnGlobal) {
installCommand = `yarn global add ${this.packageName}`;
} else if (hasYarn()()) {
installCommand = `yarn add ${this.packageName}`;
} else {
installCommand = `npm i ${options.isGlobal ? '-g ' : ''}${this.packageName}`;
}

opts.message = opts.message || 'Update available ' + chalk().dim(this.update.current) + chalk().reset(' → ') +
chalk().green(this.update.latest) + ' \nRun ' + chalk().cyan('npm i ' + (opts.isGlobal ? '-g ' : '') + this.packageName) + ' to update';
options.message = options.message || 'Update available ' + chalk().dim(this.update.current) + chalk().reset(' → ') +
chalk().green(this.update.latest) + ' \nRun ' + chalk().cyan(installCommand) + ' to update';

opts.boxenOpts = opts.boxenOpts || {
options.boxenOpts = options.boxenOpts || {
padding: 1,
margin: 1,
align: 'center',
borderColor: 'yellow',
borderStyle: 'round'
};

const message = '\n' + boxen()(opts.message, opts.boxenOpts);
const message = '\n' + boxen()(options.message, options.boxenOpts);

if (opts.defer === false) {
if (options.defer === false) {
console.error(message);
} else {
process.on('exit', () => {
108 changes: 55 additions & 53 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,55 +1,57 @@
{
"name": "update-notifier",
"version": "2.5.0",
"description": "Update notifications for your CLI app",
"license": "BSD-2-Clause",
"repository": "yeoman/update-notifier",
"author": {
"name": "Sindre Sorhus",
"email": "sindresorhus@gmail.com",
"url": "https://sindresorhus.com"
},
"engines": {
"node": ">=4"
},
"scripts": {
"test": "xo && ava --timeout=20s"
},
"files": [
"index.js",
"check.js"
],
"keywords": [
"npm",
"update",
"updater",
"notify",
"notifier",
"check",
"checker",
"cli",
"module",
"package",
"version"
],
"dependencies": {
"boxen": "^1.2.1",
"chalk": "^2.0.1",
"configstore": "^3.0.0",
"import-lazy": "^2.1.0",
"is-ci": "^1.0.10",
"is-installed-globally": "^0.1.0",
"is-npm": "^1.0.0",
"latest-version": "^3.0.0",
"semver-diff": "^2.0.0",
"xdg-basedir": "^3.0.0"
},
"devDependencies": {
"ava": "*",
"clear-module": "^2.1.0",
"fixture-stdout": "^0.2.1",
"mock-require": "^2.0.2",
"strip-ansi": "^4.0.0",
"xo": "^0.18.2"
}
"name": "update-notifier",
"version": "3.0.0",
"description": "Update notifications for your CLI app",
"license": "BSD-2-Clause",
"repository": "yeoman/update-notifier",
"author": {
"name": "Sindre Sorhus",
"email": "sindresorhus@gmail.com",
"url": "https://sindresorhus.com"
},
"engines": {
"node": ">=8"
},
"scripts": {
"test": "xo && ava --timeout=20s -s"
},
"files": [
"index.js",
"check.js"
],
"keywords": [
"npm",
"update",
"updater",
"notify",
"notifier",
"check",
"checker",
"cli",
"module",
"package",
"version"
],
"dependencies": {
"boxen": "^3.0.0",
"chalk": "^2.0.1",
"configstore": "^4.0.0",
"has-yarn": "^2.1.0",
"import-lazy": "^2.1.0",
"is-ci": "^2.0.0",
"is-installed-globally": "^0.1.0",
"is-npm": "^3.0.0",
"is-yarn-global": "^0.3.0",
"latest-version": "^5.0.0",
"semver-diff": "^2.0.0",
"xdg-basedir": "^3.0.0"
},
"devDependencies": {
"ava": "^1.3.1",
"clear-module": "^3.1.0",
"fixture-stdout": "^0.2.1",
"mock-require": "^3.0.3",
"strip-ansi": "^5.2.0",
"xo": "^0.24.0"
}
}
46 changes: 37 additions & 9 deletions readme.md
Original file line number Diff line number Diff line change
@@ -80,11 +80,26 @@ The update check is done in a unref'ed [child process](https://nodejs.org/api/ch
The first time the user runs your app, it will check for an update, and even if an update is available, it will wait the specified `updateCheckInterval` before notifying the user. This is done to not be annoying to the user, but might surprise you as an implementer if you're testing whether it works. Check out [`example.js`](example.js) to quickly test out `update-notifier` and see how you can test that it works in your app.


---

<div align="center">
<b>
<a href="https://tidelift.com/subscription/pkg/npm-update_notifier?utm_source=npm-update-notifier&utm_medium=referral&utm_campaign=readme">Get professional support for this package with a Tidelift subscription</a>
</b>
<br>
<sub>
Tidelift helps make open source sustainable for maintainers while giving companies<br>assurances about security, maintenance, and licensing for their dependencies.
</sub>
</div>

---


## API

### notifier = updateNotifier(options)

Checks if there is an available update. Accepts options defined below. Returns an instance with an `.update` property there is an available update, otherwise `undefined`.
Checks if there is an available update. Accepts options defined below. Returns an instance with an `.update` property if there is an available update, otherwise `undefined`.

### options

@@ -115,6 +130,20 @@ Type: `Function`

Passing a callback here will make it check for an update directly and report right away. Not recommended as you won't get the benefits explained in [`How`](#how). `update` is equal to `notifier.update`.

#### shouldNotifyInNpmScript

Type: `boolean`<br>
Default: `false`

Allows notification to be shown when running as an npm script.

#### distTag

Type: `string`<br>
Default: `latest`

Which [dist-tag](https://docs.npmjs.com/adding-dist-tags-to-packages) to use to find the latest version.

### notifier.notify([options])

Convenience method to display a notification message. *(See screenshot)*
@@ -142,7 +171,7 @@ Message that will be shown when an update is available.
##### isGlobal

Type: `boolean`<br>
Default: `true`
Default: auto-detect

Include the `-g` argument in the default message's `npm i` recommendation. You may want to change this if your CLI package can be installed as a dependency of another project, and don't want to recommend a global installation. This option is ignored if you supply your own `message` (see above).

@@ -153,12 +182,6 @@ Default: `{padding: 1, margin: 1, align: 'center', borderColor: 'yellow', border

Options object that will be passed to [`boxen`](https://github.com/sindresorhus/boxen).

##### shouldNotifyInNpmScript

Type: `boolean`<br>
Default: `false`

Allows notification to be shown when running as an npm script.

### User settings

@@ -185,7 +208,12 @@ There are a bunch projects using it:
- [Pageres](https://github.com/sindresorhus/pageres) - Capture website screenshots
- [Node GH](http://nodegh.io) - GitHub command line tool

[And 1600+ more…](https://www.npmjs.org/browse/depended/update-notifier)
[And 2700+ more…](https://www.npmjs.org/browse/depended/update-notifier)


## Security

To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure.


## License
5 changes: 2 additions & 3 deletions test/fs-error.js
Original file line number Diff line number Diff line change
@@ -4,9 +4,8 @@ import test from 'ava';
let updateNotifier;

test.before(() => {
['.', 'configstore', 'xdg-basedir'].forEach(clearModule);
// Set configstore.config to something
// that requires root access
['..', 'configstore', 'xdg-basedir'].forEach(clearModule);
// Set configstore.config to something that requires root access
process.env.XDG_CONFIG_HOME = '/usr';
updateNotifier = require('..');
});
Loading