Skip to content

Commit

Permalink
ES2015ify
Browse files Browse the repository at this point in the history
  • Loading branch information
sindresorhus committed Feb 13, 2017
1 parent ac47d69 commit fda96b6
Show file tree
Hide file tree
Showing 7 changed files with 174 additions and 168 deletions.
1 change: 1 addition & 0 deletions .gitattributes
@@ -1 +1,2 @@
* text=auto
*.js text eol=lf
3 changes: 1 addition & 2 deletions .travis.yml
@@ -1,5 +1,4 @@
sudo: false
language: node_js
node_js:
- '5'
- '6'
- '4'
12 changes: 6 additions & 6 deletions check.js
@@ -1,22 +1,22 @@
/* eslint-disable unicorn/no-process-exit */
'use strict';
var updateNotifier = require('./');
let updateNotifier = require('.');

var options = JSON.parse(process.argv[2]);
const options = JSON.parse(process.argv[2]);

updateNotifier = new updateNotifier.UpdateNotifier(options);

updateNotifier.checkNpm().then(function (update) {
// only update the last update check time on success
updateNotifier.checkNpm().then(update => {
// 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 nodejs docs)
// Otherwise the child process will run forever, according to the Node.js docs
process.exit();
}).catch(function () {
}).catch(() => {
process.exit(1);
});
8 changes: 6 additions & 2 deletions example.js
@@ -1,7 +1,11 @@
'use strict';
var updateNotifier = require('./');
const updateNotifier = require('.');

// you have to run this file two times the first time
// Run: $ node example

// You have to run this file two times the first time
// This is because it never reports updates on the first run
// If you want to test your own usage, ensure you set an older version

updateNotifier({
pkg: {
Expand Down
244 changes: 123 additions & 121 deletions index.js
@@ -1,144 +1,146 @@
'use strict';
var spawn = require('child_process').spawn;
var path = require('path');
var format = require('util').format;
var lazyRequire = require('lazy-req')(require);

var configstore = lazyRequire('configstore');
var chalk = lazyRequire('chalk');
var semverDiff = lazyRequire('semver-diff');
var latestVersion = lazyRequire('latest-version');
var isNpm = lazyRequire('is-npm');
var boxen = lazyRequire('boxen');
var xdgBasedir = lazyRequire('xdg-basedir');
var ONE_DAY = 1000 * 60 * 60 * 24;

function UpdateNotifier(options) {
this.options = options = options || {};
options.pkg = options.pkg || {};

// 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');
}
const spawn = require('child_process').spawn;
const path = require('path');
const format = require('util').format;
const lazyRequire = require('lazy-req')(require);

const configstore = lazyRequire('configstore');
const chalk = lazyRequire('chalk');
const semverDiff = lazyRequire('semver-diff');
const latestVersion = lazyRequire('latest-version');
const isNpm = lazyRequire('is-npm');
const boxen = lazyRequire('boxen');
const xdgBasedir = lazyRequire('xdg-basedir');
const ONE_DAY = 1000 * 60 * 60 * 24;

class UpdateNotifier {
constructor(options) {
this.options = options = options || {};
options.pkg = options.pkg || {};

// 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
};

this.packageName = options.pkg.name;
this.packageVersion = options.pkg.version;
this.updateCheckInterval = typeof options.updateCheckInterval === 'number' ? options.updateCheckInterval : ONE_DAY;
this.hasCallback = typeof options.callback === 'function';
this.callback = options.callback || function () {};

if (!this.hasCallback) {
try {
var 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 (err) {
// expecting error code EACCES or EPERM
var msg =
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', function () {
console.error('\n' + boxen()(msg, {align: 'center'}));
});
if (!options.pkg.name || !options.pkg.version) {
throw new Error('pkg.name and pkg.version required');
}
}
}

UpdateNotifier.prototype.check = function () {
if (this.hasCallback) {
this.checkNpm().then(this.callback.bind(this, null)).catch(this.callback);
return;
}
if (
!this.config ||
this.config.get('optOut') ||
'NO_UPDATE_NOTIFIER' in process.env ||
process.argv.indexOf('--no-update-notifier') !== -1
) {
return;
this.packageName = options.pkg.name;
this.packageVersion = options.pkg.version;
this.updateCheckInterval = typeof options.updateCheckInterval === 'number' ? options.updateCheckInterval : ONE_DAY;
this.hasCallback = typeof options.callback === 'function';
this.callback = options.callback || (() => {});

if (!this.hasCallback) {
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 (err) {
// Expecting error code EACCES or EPERM
const msg =
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'}));
});
}
}
}
check() {
if (this.hasCallback) {
this.checkNpm()
.then(update => this.callback(null, update))
.catch(err => this.callback(err));
return;
}

this.update = this.config.get('update');

if (this.update) {
this.config.delete('update');
}
if (
!this.config ||
this.config.get('optOut') ||
'NO_UPDATE_NOTIFIER' in process.env ||
process.argv.indexOf('--no-update-notifier') !== -1
) {
return;
}

// Only check for updates on a set interval
if (Date.now() - this.config.get('lastUpdateCheck') < this.updateCheckInterval) {
return;
}
this.update = this.config.get('update');

// 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();
};
if (this.update) {
this.config.delete('update');
}

UpdateNotifier.prototype.checkNpm = function () {
return latestVersion()(this.packageName).then(function (latestVersion) {
return {
latest: latestVersion,
current: this.packageVersion,
type: semverDiff()(this.packageVersion, latestVersion) || 'latest',
name: this.packageName
};
}.bind(this));
};
// Only check for updates on a set interval
if (Date.now() - this.config.get('lastUpdateCheck') < this.updateCheckInterval) {
return;
}

UpdateNotifier.prototype.notify = function (opts) {
if (!process.stdout.isTTY || isNpm() || !this.update) {
return this;
// 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();
}
checkNpm() {
return latestVersion()(this.packageName).then(latestVersion => {
return {
latest: latestVersion,
current: this.packageVersion,
type: semverDiff()(this.packageVersion, latestVersion) || 'latest',
name: this.packageName
};
});
}
notify(opts) {
if (!process.stdout.isTTY || isNpm() || !this.update) {
return this;
}

opts = opts || {};
opts = opts || {};

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

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

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

if (opts.defer === false) {
console.error(message);
} else {
process.on('exit', function () {
if (opts.defer === false) {
console.error(message);
});
} else {
process.on('exit', () => {
console.error(message);
});

process.on('SIGINT', function () {
console.error('');
process.exit();
});
}
process.on('SIGINT', () => {
console.error('');
process.exit();
});
}

return this;
};
return this;
}
}

module.exports = function (options) {
var updateNotifier = new UpdateNotifier(options);
module.exports = options => {
const updateNotifier = new UpdateNotifier(options);
updateNotifier.check();
return updateNotifier;
};
Expand Down
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -47,6 +47,6 @@
"fixture-stdout": "^0.2.1",
"mocha": "*",
"strip-ansi": "^3.0.1",
"xo": "*"
"xo": "^0.17.0"
}
}

1 comment on commit fda96b6

@DenisCarriere
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ES2015'fying projects is always fun 👍

Please sign in to comment.