Permalink
Browse files

Add interactive UI (#97)

  • Loading branch information...
1 parent 40bb91f commit df327744a1c48f36ff0b24a0672934e1e80eb20d @zinserjan zinserjan committed with Nov 4, 2016
Showing with 293 additions and 21 deletions.
  1. +22 −7 cli.js
  2. +1 −1 index.js
  3. +10 −12 lib/prerequisite.js
  4. +104 −0 lib/ui.js
  5. +36 −0 lib/version.js
  6. +2 −0 package.json
  7. +1 −0 readme.md
  8. +1 −1 test.js
  9. +116 −0 test/version.js
View
@@ -2,14 +2,16 @@
'use strict';
const meow = require('meow');
const updateNotifier = require('update-notifier');
+const version = require('./lib/version');
+const ui = require('./lib/ui');
const np = require('./');
const cli = meow(`
Usage
$ np <version>
Version can be:
- patch | minor | major | prepatch | preminor | premajor | prerelease | 1.2.3
+ ${version.SEMVER_INCREMENTS.join(' | ')} | 1.2.3
Options
--any-branch Allow publishing from any branch
@@ -18,19 +20,32 @@ const cli = meow(`
--tag Publish under a given dist-tag
Examples
+ $ np
$ np patch
$ np 1.0.2
$ np 1.0.2-beta.3 --tag=beta
`);
updateNotifier({pkg: cli.pkg}).notify();
-if (cli.input.length === 0) {
- console.error('Specify a version\n\nExample: $ np patch');
- process.exit(1);
-}
-
-np(cli.input[0], cli.flags)
+Promise
+ .resolve()
+ .then(() => {
+ if (cli.input.length !== 0) {
+ return Object.assign({}, cli.flags, {
+ confirm: true,
+ version: cli.input[0]
+ });
+ }
+ return ui(cli.flags);
+ })
+ .then(options => {
+ if (!options.confirm) {
+ process.exit(0);
+ }
+ return options;
+ })
+ .then(options => np(options.version, options))
.then(pkg => {
console.log(`\n ${pkg.name} ${pkg.version} published`);
})
View
@@ -3,7 +3,7 @@ const execa = require('execa');
const del = require('del');
const Listr = require('listr');
const split = require('split');
-require('any-observable/register/rxjs-all');
+require('any-observable/register/rxjs-all'); // eslint-disable-line import/no-unassigned-import
const Observable = require('any-observable');
const streamToObservable = require('stream-to-observable');
const readPkgUp = require('read-pkg-up');
View
@@ -1,41 +1,39 @@
'use strict';
-const semver = require('semver');
const execa = require('execa');
const Listr = require('listr');
-
-const VERSIONS = ['major', 'minor', 'patch', 'premajor', 'preminor', 'prepatch', 'prerelease'];
-const PRERELEASE_VERSIONS = ['premajor', 'preminor', 'prepatch', 'prerelease'];
+const version = require('./version');
module.exports = (input, pkg, opts) => {
- const newVersion = VERSIONS.indexOf(input) === -1 ? input : semver.inc(pkg.version, input);
-
+ let newVersion = null;
const tasks = [
{
title: 'Validate version',
task: () => {
- if (VERSIONS.indexOf(input) === -1 && !semver.valid(input)) {
- throw new Error(`Version should be either ${VERSIONS.join(', ')}, or a valid semver version.`);
+ if (!version.isValidVersionInput(input)) {
+ throw new Error(`Version should be either ${version.SEMVER_INCREMENTS.join(', ')}, or a valid semver version.`);
}
- if (semver.gte(pkg.version, newVersion)) {
+ newVersion = version.getNewVersion(pkg.version, input);
+
+ if (!version.isVersionGreater(pkg.version, newVersion)) {
throw new Error(`New version \`${newVersion}\` should be higher than current version \`${pkg.version}\``);
}
}
},
{
title: 'Check for pre-release version',
task: () => {
- if ((PRERELEASE_VERSIONS.indexOf(input) !== -1 || semver.prerelease(input)) && !opts.tag) {
+ if (!pkg.private && version.isPrereleaseVersion(newVersion) && !opts.tag) {
throw new Error('You must specify a dist-tag using --tag when publishing a pre-release version. This prevents accidentally tagging unstable versions as "latest". https://docs.npmjs.com/cli/dist-tag');
}
}
},
{
title: 'Check npm version',
- skip: () => semver.lt(process.version, '6.0.0'),
+ skip: () => version.isVersionLower('6.0.0', process.version),
task: () => execa.stdout('npm', ['version', '--json']).then(json => {
const versions = JSON.parse(json);
- if (!semver.satisfies(versions.npm, '>=2.15.8 <3.0.0 || >=3.10.1')) {
+ if (!version.satisfies(versions.npm, '>=2.15.8 <3.0.0 || >=3.10.1')) {
throw new Error(`npm@${versions.npm} has known issues publishing when running Node.js 6. Please upgrade npm or downgrade Node and publish again. https://github.com/npm/npm/issues/5082`);
}
})
View
@@ -0,0 +1,104 @@
+'use strict';
+const execa = require('execa');
+const inquirer = require('inquirer');
+const readPkgUp = require('read-pkg-up');
+const chalk = require('chalk');
+const version = require('./version');
+
+module.exports = options => {
+ const pkg = readPkgUp.sync().pkg;
+ const oldVersion = pkg.version;
+
+ console.log('\n');
+ console.log(`Releasing a new version of ${chalk.bold(pkg.name)} ${chalk.gray.dim(`(current version: ${oldVersion})`)}`);
+ console.log('\n');
+
+ const prompts = [
+ {
+ type: 'list',
+ name: 'version',
+ message: 'Select semver increment or specify new version',
+ choices: version.SEMVER_INCREMENTS
+ .map(inc => ({
+ name: `${inc} ${chalk.gray.dim(`(${version.getNewVersion(oldVersion, inc)})`)}`,
+ value: inc
+ }))
+ .concat([
+ new inquirer.Separator(),
+ {
+ name: 'Other (specify)',
+ value: null
+ }
+ ]),
+ filter: input => version.isValidVersionInput(input) ? version.getNewVersion(oldVersion, input) : input
+ },
+ {
+ type: 'input',
+ name: 'version',
+ message: 'Version',
+ when: answers => !answers.version,
+ filter: input => version.isValidVersionInput(input) ? version.getNewVersion(pkg.version, input) : input,
+ validate: input => {
+ if (!version.isValidVersionInput(input)) {
+ return 'Please specify a valid semver, e.g. 1.2.3. See http://semver.org';
+ } else if (!version.isVersionGreater(oldVersion, input)) {
+ return `Version must be greater than ${oldVersion}`;
+ }
+ return true;
+ }
+ },
+ {
+ type: 'list',
+ name: 'tag',
+ message: 'How should this pre-release version be tagged in npm?',
+ when: answers => !pkg.private && version.isPrereleaseVersion(answers.version) && !options.tag,
+ choices: () => execa.stdout('npm', ['dist-tag', 'ls'])
+ .then(stdout => {
+ const existingPrereleaseTags = stdout.split('\n')
+ .map(line => line.split(':')[0].replace(/^\s|\s$/, ''))
+ .filter(line => line.toLowerCase() !== 'latest');
+
+ if (existingPrereleaseTags.length === 0) {
+ existingPrereleaseTags.push('next');
+ }
+
+ return existingPrereleaseTags
+ .concat([
+ new inquirer.Separator(),
+ {
+ name: 'Other (specify)',
+ value: null
+ }
+ ]);
+ })
+ },
+ {
+ type: 'input',
+ name: 'tag',
+ message: 'Tag',
+ when: answers => !pkg.private && version.isPrereleaseVersion(answers.version) && !options.tag && !answers.tag,
+ validate: input => {
+ if (input.length === 0) {
+ return 'Please specify a tag, e.g. next.';
+ } else if (input.toLowerCase() === 'latest') {
+ return 'It\'s not possible to publish pre-releases under the latest tag. Please specifiy something else, e.g. next.';
+ }
+ return true;
+ }
+ },
+ {
+ type: 'confirm',
+ name: 'confirm',
+ message: answers => {
+ const tag = answers.tag || options.tag;
+ const tagPart = tag ? ` and tag this release in npm as ${tag}` : '';
+
+ return `Will bump from ${oldVersion} to ${answers.version}${tagPart}. Continue?`;
+ }
+ }
+ ];
+
+ return inquirer
+ .prompt(prompts)
+ .then(answers => Object.assign({}, options, answers));
+};
View
@@ -0,0 +1,36 @@
+'use strict';
+const semver = require('semver');
+
+exports.SEMVER_INCREMENTS = ['patch', 'minor', 'major', 'prepatch', 'preminor', 'premajor', 'prerelease'];
+exports.PRERELEASE_VERSIONS = ['prepatch', 'preminor', 'premajor', 'prerelease'];
+
+function isValidVersion(input) {
+ return Boolean(semver.valid(input));
+}
+
+exports.isValidVersionInput = input => exports.SEMVER_INCREMENTS.indexOf(input) !== -1 || isValidVersion(input);
+
+exports.isPrereleaseVersion = version => exports.PRERELEASE_VERSIONS.indexOf(version) !== -1 || Boolean(semver.prerelease(version));
+
+exports.getNewVersion = (oldVersion, input) => {
+ if (!exports.isValidVersionInput(input)) {
+ throw new Error(`Version should be either ${exports.SEMVER_INCREMENTS.join(', ')} or a valid semver version.`);
+ }
+ return exports.SEMVER_INCREMENTS.indexOf(input) === -1 ? input : semver.inc(oldVersion, input);
+};
+
+exports.isVersionGreater = (oldVersion, newVersion) => {
+ if (!isValidVersion(newVersion)) {
+ throw new Error('Version should be a valid semver version.');
+ }
+ return semver.gt(newVersion, oldVersion);
+};
+
+exports.isVersionLower = (oldVersion, newVersion) => {
+ if (!isValidVersion(newVersion)) {
+ throw new Error('Version should be a valid semver version.');
+ }
+ return semver.lt(newVersion, oldVersion);
+};
+
+exports.satisfies = (version, range) => semver.satisfies(version, range);
View
@@ -41,8 +41,10 @@
],
"dependencies": {
"any-observable": "^0.2.0",
+ "chalk": "^1.1.3",
"del": "^2.2.0",
"execa": "^0.4.0",
+ "inquirer": "^1.2.1",
"listr": "^0.6.1",
"meow": "^3.7.0",
"read-pkg-up": "^1.0.1",
View
@@ -42,6 +42,7 @@ $ np --help
--tag Publish under a given dist-tag
Examples
+ $ np
$ np patch
$ np 1.0.2
$ np 1.0.2-beta.3 --tag=beta
View
@@ -2,7 +2,7 @@ import test from 'ava';
import m from './';
test('version is invalid', t => {
- const message = 'Version should be either major, minor, patch, premajor, preminor, prepatch, prerelease, or a valid semver version.';
+ const message = 'Version should be either patch, minor, major, prepatch, preminor, premajor, prerelease, or a valid semver version.';
t.throws(m('foo'), message);
t.throws(m('4.x.3'), message);
});
Oops, something went wrong.

0 comments on commit df32774

Please sign in to comment.