diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 28f5aa6c..2d58c737 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -10,14 +10,12 @@ jobs:
fail-fast: false
matrix:
node-version:
- - 14
- - 12
- - 10
+ - 19
+ - 18
+ - 16
steps:
- - uses: actions/checkout@v2
- with:
- submodules: true
- - uses: actions/setup-node@v2
+ - uses: actions/checkout@v3
+ - uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- run: git config --global user.name "Github Actions"
diff --git a/.gitmodules b/.gitmodules
deleted file mode 100644
index 18f98e71..00000000
--- a/.gitmodules
+++ /dev/null
@@ -1,3 +0,0 @@
-[submodule "integration-test"]
- path = integration-test
- url = https://github.com/bunysae/np_integration_test
diff --git a/integration-test b/integration-test
deleted file mode 160000
index b2492e19..00000000
--- a/integration-test
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit b2492e190f3864f96955a7acb4349bb9722530b9
diff --git a/private-packages.png b/media/private-packages.png
similarity index 100%
rename from private-packages.png
rename to media/private-packages.png
diff --git a/screenshot-ui.png b/media/screenshot-ui.png
similarity index 100%
rename from screenshot-ui.png
rename to media/screenshot-ui.png
diff --git a/screenshot.gif b/media/screenshot.gif
similarity index 100%
rename from screenshot.gif
rename to media/screenshot.gif
diff --git a/package.json b/package.json
index 22158766..219d79d7 100644
--- a/package.json
+++ b/package.json
@@ -5,15 +5,16 @@
"license": "MIT",
"repository": "sindresorhus/np",
"funding": "https://github.com/sindresorhus/np?sponsor=1",
+ "type": "module",
"bin": "source/cli.js",
"engines": {
- "node": ">=10",
- "npm": ">=6.8.0",
+ "node": ">=16.6.0",
+ "npm": ">=7.19.0",
"git": ">=2.11.0",
"yarn": ">=1.7.0"
},
"scripts": {
- "test": "xo && FORCE_HYPERLINK=1 ava"
+ "test": "xo && ava && ava test/integration.js --no-worker-threads"
},
"files": [
"source"
@@ -30,63 +31,60 @@
"commit"
],
"dependencies": {
- "@samverschueren/stream-to-observable": "^0.3.1",
- "any-observable": "^0.5.1",
- "async-exit-hook": "^2.0.1",
- "chalk": "^4.1.0",
- "cosmiconfig": "^7.0.0",
- "del": "^6.0.0",
- "escape-goat": "^3.0.0",
- "escape-string-regexp": "^4.0.0",
- "execa": "^5.0.0",
+ "chalk": "^5.2.0",
+ "cosmiconfig": "^8.1.3",
+ "del": "^7.0.0",
+ "escape-goat": "^4.0.0",
+ "escape-string-regexp": "^5.0.0",
+ "execa": "^7.1.1",
+ "exit-hook": "^3.2.0",
"github-url-from-git": "^1.5.0",
- "has-yarn": "^2.1.0",
- "hosted-git-info": "^3.0.7",
- "ignore-walk": "^3.0.3",
- "import-local": "^3.0.2",
- "inquirer": "^7.3.3",
- "is-installed-globally": "^0.3.2",
- "is-interactive": "^1.0.0",
- "is-scoped": "^2.1.0",
- "issue-regex": "^3.1.0",
+ "has-yarn": "^3.0.0",
+ "hosted-git-info": "^6.1.1",
+ "ignore-walk": "^6.0.2",
+ "import-local": "^3.1.0",
+ "inquirer": "^9.1.5",
+ "is-installed-globally": "^0.4.0",
+ "is-interactive": "^2.0.0",
+ "is-scoped": "^3.0.0",
+ "issue-regex": "^4.1.0",
"listr": "^0.14.3",
"listr-input": "^0.2.1",
- "log-symbols": "^4.0.0",
- "meow": "^8.1.0",
- "minimatch": "^3.0.4",
- "new-github-release-url": "^1.0.0",
- "npm-name": "^6.0.1",
- "onetime": "^5.1.2",
- "open": "^7.3.0",
- "ow": "^0.21.0",
- "p-memoize": "^4.0.1",
- "p-timeout": "^4.1.0",
- "pkg-dir": "^5.0.0",
- "read-pkg-up": "^7.0.1",
- "rxjs": "^6.6.3",
- "semver": "^7.3.4",
- "split": "^1.0.1",
- "symbol-observable": "^3.0.0",
- "terminal-link": "^2.1.1",
- "update-notifier": "^5.0.1"
+ "log-symbols": "^5.1.0",
+ "meow": "^11.0.0",
+ "minimatch": "^8.0.2",
+ "new-github-release-url": "^2.0.0",
+ "npm-name": "^7.1.0",
+ "onetime": "^6.0.0",
+ "open": "^9.1.0",
+ "ow": "^1.1.1",
+ "p-memoize": "^7.1.1",
+ "p-timeout": "^6.1.1",
+ "pkg-dir": "^7.0.0",
+ "read-pkg-up": "^9.1.0",
+ "rxjs": "^7.8.0",
+ "semver": "^7.3.8",
+ "symbol-observable": "^4.0.0",
+ "terminal-link": "^3.0.0",
+ "update-notifier": "^6.0.2"
},
"devDependencies": {
- "ava": "^2.3.0",
- "execa_test_double": "^4.0.1",
- "mockery": "^2.1.0",
- "proxyquire": "^2.1.3",
- "sinon": "^9.2.2",
- "xo": "^0.36.1"
+ "ava": "^5.2.0",
+ "common-tags": "^1.8.2",
+ "esmock": "^2.2.0",
+ "fs-extra": "^11.1.1",
+ "sinon": "^15.0.3",
+ "xo": "^0.53.1"
},
"ava": {
+ "environmentVariables": {
+ "FORCE_HYPERLINK": "1"
+ },
"files": [
- "!test/fixtures",
- "!integration-test"
- ]
- },
- "xo": {
- "ignores": [
- "integration-test"
+ "!test/integration.js"
+ ],
+ "nodeArguments": [
+ "--loader=esmock"
]
}
}
diff --git a/readme.md b/readme.md
index de2d6736..f04dcf7a 100644
--- a/readme.md
+++ b/readme.md
@@ -22,7 +22,7 @@
---
-
+
## Why
@@ -54,8 +54,8 @@
## Prerequisite
-- Node.js 10 or later
-- npm 6.8.0 or later
+- Node.js 16 or later
+- npm 7.19.0 or later
- Git 2.11 or later
## Install
@@ -104,11 +104,11 @@ $ np --help
Run `np` without arguments to launch the interactive UI that guides you through publishing a new version.
-
+
## Config
-`np` can be configured both locally and globally. When using the global `np` binary, you can configure any of the CLI flags in either a `.np-config.js`, `.np-config.cjs` or `.np-config.json` file in the home directory. When using the local `np` binary, for example, in a `npm run` script, you can configure `np` by setting the flags in either a top-level `np` field in `package.json` or in a `.np-config.js`, `.np-config.cjs` or `.np-config.json` file in the project directory. If it exists, the local installation will always take precedence. This ensures any local config matches the version of `np` it was designed for.
+`np` can be configured both globally and locally. When using the global `np` binary, you can configure any of the CLI flags in either a `.np-config.js` (as CJS), `.np-config.cjs`, `.np-config.mjs`, or `.np-config.json` file in the home directory. When using the local `np` binary, for example, in a `npm run` script, you can configure `np` by setting the flags in either a top-level `np` field in `package.json` or in one of the aforementioned file types in the project directory. If it exists, the local installation will always take precedence. This ensures any local config matches the version of `np` it was designed for.
Currently, these are the flags you can configure:
@@ -156,6 +156,14 @@ module.exports = {
};
```
+`.np-config.mjs`
+```js
+export default {
+ yarn: false,
+ contents: 'dist'
+};
+```
+
_**Note:** The global config only applies when using the global `np` binary, and is never inherited when using a local binary._
## Tips
@@ -226,7 +234,7 @@ $ yarn config set version-sign-git-tag true
### Private packages
-
+
You can use `np` for packages that aren't publicly published to npm (perhaps installed from a private git repo).
diff --git a/source/cli-implementation.js b/source/cli-implementation.js
index 2e541542..3b394f92 100755
--- a/source/cli-implementation.js
+++ b/source/cli-implementation.js
@@ -1,25 +1,25 @@
#!/usr/bin/env node
-'use strict';
// eslint-disable-next-line import/no-unassigned-import
-require('symbol-observable'); // Important: This needs to be first to prevent weird Observable incompatibilities
-const logSymbols = require('log-symbols');
-const meow = require('meow');
-const updateNotifier = require('update-notifier');
-const hasYarn = require('has-yarn');
-const config = require('./config');
-const git = require('./git-util');
-const {isPackageNameAvailable} = require('./npm/util');
-const version = require('./version');
-const util = require('./util');
-const ui = require('./ui');
-const np = require('.');
+import 'symbol-observable'; // Important: This needs to be first to prevent weird Observable incompatibilities
+import logSymbols from 'log-symbols';
+import meow from 'meow';
+import updateNotifier from 'update-notifier';
+import hasYarn from 'has-yarn';
+import {gracefulExit} from 'exit-hook';
+import config from './config.js';
+import * as git from './git-util.js';
+import {isPackageNameAvailable} from './npm/util.js';
+import Version from './version.js';
+import * as util from './util.js';
+import ui from './ui.js';
+import np from './index.js';
const cli = meow(`
Usage
$ np
Version can be:
- ${version.SEMVER_INCREMENTS.join(' | ')} | 1.2.3
+ ${Version.SEMVER_INCREMENTS.join(' | ')} | 1.2.3
Options
--any-branch Allow publishing from any branch
@@ -45,60 +45,61 @@ const cli = meow(`
$ np 1.0.2-beta.3 --tag=beta
$ np 1.0.2-beta.3 --tag=beta --contents=dist
`, {
+ importMeta: import.meta,
booleanDefault: undefined,
flags: {
anyBranch: {
- type: 'boolean'
+ type: 'boolean',
},
branch: {
- type: 'string'
+ type: 'string',
},
cleanup: {
- type: 'boolean'
+ type: 'boolean',
},
tests: {
- type: 'boolean'
+ type: 'boolean',
},
yolo: {
- type: 'boolean'
+ type: 'boolean',
},
publish: {
- type: 'boolean'
+ type: 'boolean',
},
releaseDraft: {
- type: 'boolean'
+ type: 'boolean',
},
releaseDraftOnly: {
- type: 'boolean'
+ type: 'boolean',
},
tag: {
- type: 'string'
+ type: 'string',
},
yarn: {
- type: 'boolean'
+ type: 'boolean',
},
contents: {
- type: 'string'
+ type: 'string',
},
preview: {
- type: 'boolean'
+ type: 'boolean',
},
testScript: {
- type: 'string'
+ type: 'string',
},
'2fa': {
- type: 'boolean'
+ type: 'boolean',
},
message: {
- type: 'string'
- }
- }
+ type: 'string',
+ },
+ },
});
updateNotifier({pkg: cli.pkg}).notify();
-(async () => {
- const {pkg, pkgPath} = util.readPkg();
+try {
+ const {pkg, pkgPath} = await util.readPkg();
const defaultFlags = {
cleanup: true,
@@ -106,7 +107,7 @@ updateNotifier({pkg: cli.pkg}).notify();
publish: true,
releaseDraft: true,
yarn: hasYarn(),
- '2fa': true
+ '2fa': true,
};
const localConfig = await config();
@@ -114,7 +115,7 @@ updateNotifier({pkg: cli.pkg}).notify();
const flags = {
...defaultFlags,
...localConfig,
- ...cli.flags
+ ...cli.flags,
};
// Workaround for unintended auto-casing behavior from `meow`.
@@ -126,7 +127,7 @@ updateNotifier({pkg: cli.pkg}).notify();
const availability = flags.publish ? await isPackageNameAvailable(pkg) : {
isAvailable: false,
- isUnknown: false
+ isUnknown: false,
};
// Use current (latest) version when 'releaseDraftOnly', otherwise use the first argument.
@@ -138,22 +139,22 @@ updateNotifier({pkg: cli.pkg}).notify();
availability,
version,
runPublish,
- branch
+ branch,
}, {pkg, pkgPath});
if (!options.confirm) {
- process.exit(0);
+ gracefulExit();
}
console.log(); // Prints a newline for readability
const newPkg = await np(options.version, options);
if (options.preview || options.releaseDraftOnly) {
- return;
+ gracefulExit();
}
console.log(`\n ${newPkg.name} ${newPkg.version} published 🎉`);
-})().catch(error => {
+} catch (error) {
console.error(`\n${logSymbols.error} ${error.message}`);
- process.exit(1);
-});
+ gracefulExit(1);
+}
diff --git a/source/cli.js b/source/cli.js
index f79dcc86..8c71be61 100755
--- a/source/cli.js
+++ b/source/cli.js
@@ -1,9 +1,10 @@
#!/usr/bin/env node
-'use strict';
-const {debuglog} = require('util');
-const importLocal = require('import-local');
-const isInstalledGlobally = require('is-installed-globally');
+import {fileURLToPath} from 'node:url';
+import {debuglog} from 'node:util';
+import importLocal from 'import-local';
+import isInstalledGlobally from 'is-installed-globally';
+const __filename = fileURLToPath(import.meta.url);
const log = debuglog('np');
// Prefer the local installation
@@ -12,6 +13,5 @@ if (!importLocal(__filename)) {
log('Using global install of np.');
}
- // eslint-disable-next-line import/no-unassigned-import
- require('./cli-implementation');
+ await import('./cli-implementation.js');
}
diff --git a/source/config.js b/source/config.js
index b88fcfd3..cd78f7b8 100644
--- a/source/config.js
+++ b/source/config.js
@@ -1,21 +1,32 @@
-'use strict';
-const os = require('os');
-const isInstalledGlobally = require('is-installed-globally');
-const pkgDir = require('pkg-dir');
-const {cosmiconfig} = require('cosmiconfig');
+import os from 'node:os';
+import isInstalledGlobally from 'is-installed-globally';
+import {packageDirectory} from 'pkg-dir';
+import {cosmiconfig} from 'cosmiconfig';
-module.exports = async () => {
- const searchDir = isInstalledGlobally ? os.homedir() : await pkgDir();
- const searchPlaces = ['.np-config.json', '.np-config.js', '.np-config.cjs'];
+// TODO: remove when cosmiconfig/cosmiconfig#283 lands
+const loadESM = async filepath => {
+ const module = await import(filepath);
+ return module.default ?? module;
+};
+
+const getConfig = async () => {
+ const searchDir = isInstalledGlobally ? os.homedir() : await packageDirectory();
+ const searchPlaces = ['.np-config.json', '.np-config.js', '.np-config.cjs', '.np-config.mjs'];
if (!isInstalledGlobally) {
searchPlaces.push('package.json');
}
const explorer = cosmiconfig('np', {
searchPlaces,
- stopDir: searchDir
+ stopDir: searchDir,
+ loaders: {
+ '.js': loadESM,
+ '.mjs': loadESM,
+ },
});
const {config} = (await explorer.search(searchDir)) || {};
return config;
};
+
+export default getConfig;
diff --git a/source/git-tasks.js b/source/git-tasks.js
index 8f7376f6..8106685a 100644
--- a/source/git-tasks.js
+++ b/source/git-tasks.js
@@ -1,21 +1,20 @@
-'use strict';
-const Listr = require('listr');
-const git = require('./git-util');
+import Listr from 'listr';
+import * as git from './git-util.js';
-module.exports = options => {
+const gitTasks = options => {
const tasks = [
{
title: 'Check current branch',
- task: () => git.verifyCurrentBranchIsReleaseBranch(options.branch)
+ task: () => git.verifyCurrentBranchIsReleaseBranch(options.branch),
},
{
title: 'Check local working tree',
- task: () => git.verifyWorkingTreeIsClean()
+ task: () => git.verifyWorkingTreeIsClean(),
},
{
title: 'Check remote history',
- task: () => git.verifyRemoteHistoryIsClean()
- }
+ task: () => git.verifyRemoteHistoryIsClean(),
+ },
];
if (options.anyBranch) {
@@ -24,3 +23,5 @@ module.exports = options => {
return new Listr(tasks);
};
+
+export default gitTasks;
diff --git a/source/git-util.js b/source/git-util.js
index 693c88dd..917655ea 100644
--- a/source/git-util.js
+++ b/source/git-util.js
@@ -1,24 +1,23 @@
-'use strict';
-const path = require('path');
-const execa = require('execa');
-const escapeStringRegexp = require('escape-string-regexp');
-const ignoreWalker = require('ignore-walk');
-const pkgDir = require('pkg-dir');
-const {verifyRequirementSatisfied} = require('./version');
-
-exports.latestTag = async () => {
+import path from 'node:path';
+import {execa} from 'execa';
+import escapeStringRegexp from 'escape-string-regexp';
+import ignoreWalker from 'ignore-walk';
+import {packageDirectorySync} from 'pkg-dir';
+import Version from './version.js';
+
+export const latestTag = async () => {
const {stdout} = await execa('git', ['describe', '--abbrev=0', '--tags']);
return stdout;
};
-exports.root = async () => {
+export const root = async () => {
const {stdout} = await execa('git', ['rev-parse', '--show-toplevel']);
return stdout;
};
-exports.newFilesSinceLastRelease = async () => {
+export const newFilesSinceLastRelease = async () => {
try {
- const {stdout} = await execa('git', ['diff', '--name-only', '--diff-filter=A', await this.latestTag(), 'HEAD']);
+ const {stdout} = await execa('git', ['diff', '--name-only', '--diff-filter=A', await latestTag(), 'HEAD']);
if (stdout.trim().length === 0) {
return [];
}
@@ -28,15 +27,15 @@ exports.newFilesSinceLastRelease = async () => {
} catch {
// Get all files under version control
return ignoreWalker({
- path: pkgDir.sync(),
- ignoreFiles: ['.gitignore']
+ path: packageDirectorySync(),
+ ignoreFiles: ['.gitignore'],
});
}
};
-exports.readFileFromLastRelease = async file => {
- const filePathFromRoot = path.relative(await exports.root(), file);
- const {stdout: oldFile} = await execa('git', ['show', `${await this.latestTag()}:${filePathFromRoot}`]);
+export const readFileFromLastRelease = async file => {
+ const filePathFromRoot = path.relative(await root(), file);
+ const {stdout: oldFile} = await execa('git', ['show', `${await latestTag()}:${filePathFromRoot}`]);
return oldFile;
};
@@ -45,8 +44,8 @@ const firstCommit = async () => {
return stdout;
};
-exports.previousTagOrFirstCommit = async () => {
- const tags = await exports.tagList();
+export const previousTagOrFirstCommit = async () => {
+ const tags = await tagList();
if (tags.length === 0) {
return;
@@ -58,7 +57,7 @@ exports.previousTagOrFirstCommit = async () => {
try {
// Return the tag before the latest one.
- const latest = await exports.latestTag();
+ const latest = await latestTag();
const index = tags.indexOf(latest);
return tags[index - 1];
} catch {
@@ -67,11 +66,11 @@ exports.previousTagOrFirstCommit = async () => {
}
};
-exports.latestTagOrFirstCommit = async () => {
+export const latestTagOrFirstCommit = async () => {
let latest;
try {
// In case a previous tag exists, we use it to compare the current repo status to.
- latest = await exports.latestTag();
+ latest = await latestTag();
} catch {
// Otherwise, we fallback to using the first commit for comparison.
latest = await firstCommit();
@@ -80,32 +79,32 @@ exports.latestTagOrFirstCommit = async () => {
return latest;
};
-exports.hasUpstream = async () => {
- const escapedCurrentBranch = escapeStringRegexp(await exports.currentBranch());
+export const hasUpstream = async () => {
+ const escapedCurrentBranch = escapeStringRegexp(await getCurrentBranch());
const {stdout} = await execa('git', ['status', '--short', '--branch', '--porcelain']);
return new RegExp(String.raw`^## ${escapedCurrentBranch}\.\.\..+\/${escapedCurrentBranch}`).test(stdout);
};
-exports.currentBranch = async () => {
+export const getCurrentBranch = async () => {
const {stdout} = await execa('git', ['symbolic-ref', '--short', 'HEAD']);
return stdout;
};
-exports.verifyCurrentBranchIsReleaseBranch = async releaseBranch => {
- const currentBranch = await exports.currentBranch();
+export const verifyCurrentBranchIsReleaseBranch = async releaseBranch => {
+ const currentBranch = await getCurrentBranch();
if (currentBranch !== releaseBranch) {
throw new Error(`Not on \`${releaseBranch}\` branch. Use --any-branch to publish anyway, or set a different release branch using --branch.`);
}
};
-exports.tagList = async () => {
+export const tagList = async () => {
// Returns the list of tags, sorted by creation date in ascending order.
const {stdout} = await execa('git', ['tag', '--sort=creatordate']);
return stdout.split('\n');
};
-exports.isHeadDetached = async () => {
+export const isHeadDetached = async () => {
try {
// Command will fail with code 1 if the HEAD is detached.
await execa('git', ['symbolic-ref', '--quiet', 'HEAD']);
@@ -115,7 +114,7 @@ exports.isHeadDetached = async () => {
}
};
-exports.isWorkingTreeClean = async () => {
+export const isWorkingTreeClean = async () => {
try {
const {stdout: status} = await execa('git', ['status', '--porcelain']);
if (status !== '') {
@@ -128,13 +127,13 @@ exports.isWorkingTreeClean = async () => {
}
};
-exports.verifyWorkingTreeIsClean = async () => {
- if (!(await exports.isWorkingTreeClean())) {
+export const verifyWorkingTreeIsClean = async () => {
+ if (!(await isWorkingTreeClean())) {
throw new Error('Unclean working tree. Commit or stash changes first.');
}
};
-exports.isRemoteHistoryClean = async () => {
+export const isRemoteHistoryClean = async () => {
let history;
try { // Gracefully handle no remote set up.
const {stdout} = await execa('git', ['rev-list', '--count', '--left-only', '@{u}...HEAD']);
@@ -148,13 +147,13 @@ exports.isRemoteHistoryClean = async () => {
return true;
};
-exports.verifyRemoteHistoryIsClean = async () => {
- if (!(await exports.isRemoteHistoryClean())) {
+export const verifyRemoteHistoryIsClean = async () => {
+ if (!(await isRemoteHistoryClean())) {
throw new Error('Remote history differs. Please pull changes.');
}
};
-exports.verifyRemoteIsValid = async () => {
+export const verifyRemoteIsValid = async () => {
try {
await execa('git', ['ls-remote', 'origin', 'HEAD']);
} catch (error) {
@@ -162,11 +161,11 @@ exports.verifyRemoteIsValid = async () => {
}
};
-exports.fetch = async () => {
+export const fetch = async () => {
await execa('git', ['fetch']);
};
-exports.tagExistsOnRemote = async tagName => {
+export const tagExistsOnRemote = async tagName => {
try {
const {stdout: revInfo} = await execa('git', ['rev-parse', '--quiet', '--verify', `refs/tags/${tagName}`]);
@@ -192,7 +191,7 @@ async function hasLocalBranch(branch) {
'show-ref',
'--verify',
'--quiet',
- `refs/heads/${branch}`
+ `refs/heads/${branch}`,
]);
return true;
} catch {
@@ -200,7 +199,7 @@ async function hasLocalBranch(branch) {
}
}
-exports.defaultBranch = async () => {
+export const defaultBranch = async () => {
for (const branch of ['main', 'master', 'gh-pages']) {
// eslint-disable-next-line no-await-in-loop
if (await hasLocalBranch(branch)) {
@@ -209,24 +208,24 @@ exports.defaultBranch = async () => {
}
throw new Error(
- 'Could not infer the default Git branch. Please specify one with the --branch flag or with a np config.'
+ 'Could not infer the default Git branch. Please specify one with the --branch flag or with a np config.',
);
};
-exports.verifyTagDoesNotExistOnRemote = async tagName => {
- if (await exports.tagExistsOnRemote(tagName)) {
+export const verifyTagDoesNotExistOnRemote = async tagName => {
+ if (await tagExistsOnRemote(tagName)) {
throw new Error(`Git tag \`${tagName}\` already exists.`);
}
};
-exports.commitLogFromRevision = async revision => {
+export const commitLogFromRevision = async revision => {
const {stdout} = await execa('git', ['log', '--format=%s %h', `${revision}..HEAD`]);
return stdout;
};
-exports.pushGraceful = async remoteIsOnGitHub => {
+export const pushGraceful = async remoteIsOnGitHub => {
try {
- await exports.push();
+ await push();
} catch (error) {
if (remoteIsOnGitHub && error.stderr && error.stderr.includes('GH006')) {
// Try to push tags only, when commits can't be pushed due to branch protection
@@ -238,15 +237,15 @@ exports.pushGraceful = async remoteIsOnGitHub => {
}
};
-exports.push = async () => {
+export const push = async () => {
await execa('git', ['push', '--follow-tags']);
};
-exports.deleteTag = async tagName => {
+export const deleteTag = async tagName => {
await execa('git', ['tag', '--delete', tagName]);
};
-exports.removeLastCommit = async () => {
+export const removeLastCommit = async () => {
await execa('git', ['reset', '--hard', 'HEAD~1']);
};
@@ -256,13 +255,13 @@ const gitVersion = async () => {
return match && match.groups.version;
};
-exports.verifyRecentGitVersion = async () => {
+export const verifyRecentGitVersion = async () => {
const installedVersion = await gitVersion();
- verifyRequirementSatisfied('git', installedVersion);
+ Version.verifyRequirementSatisfied('git', installedVersion);
};
-exports.checkIfFileGitIgnored = async pathToFile => {
+export const checkIfFileGitIgnored = async pathToFile => {
try {
const {stdout} = await execa('git', ['check-ignore', pathToFile]);
return Boolean(stdout);
diff --git a/source/index.js b/source/index.js
index 7d25820c..a3663b01 100644
--- a/source/index.js
+++ b/source/index.js
@@ -1,43 +1,34 @@
-'use strict';
-require('any-observable/register/rxjs-all');
-const fs = require('fs');
-const path = require('path');
-const execa = require('execa');
-const del = require('del');
-const Listr = require('listr');
-const split = require('split');
-const {merge, throwError} = require('rxjs');
-const {catchError, filter, finalize} = require('rxjs/operators');
-const streamToObservable = require('@samverschueren/stream-to-observable');
-const readPkgUp = require('read-pkg-up');
-const hasYarn = require('has-yarn');
-const pkgDir = require('pkg-dir');
-const hostedGitInfo = require('hosted-git-info');
-const onetime = require('onetime');
-const exitHook = require('async-exit-hook');
-const logSymbols = require('log-symbols');
-const prerequisiteTasks = require('./prerequisite-tasks');
-const gitTasks = require('./git-tasks');
-const publish = require('./npm/publish');
-const enable2fa = require('./npm/enable-2fa');
-const npm = require('./npm/util');
-const releaseTaskHelper = require('./release-task-helper');
-const util = require('./util');
-const git = require('./git-util');
+import fs from 'node:fs';
+import path from 'node:path';
+import {execa} from 'execa';
+import {deleteAsync} from 'del';
+import Listr from 'listr';
+import {merge, throwError, catchError, filter, finalize} from 'rxjs';
+import {readPackageUp} from 'read-pkg-up';
+import hasYarn from 'has-yarn';
+import {packageDirectorySync} from 'pkg-dir';
+import hostedGitInfo from 'hosted-git-info';
+import onetime from 'onetime';
+import {asyncExitHook} from 'exit-hook';
+import logSymbols from 'log-symbols';
+import prerequisiteTasks from './prerequisite-tasks.js';
+import gitTasks from './git-tasks.js';
+import publish from './npm/publish.js';
+import enable2fa from './npm/enable-2fa.js';
+import * as npm from './npm/util.js';
+import releaseTaskHelper from './release-task-helper.js';
+import * as util from './util.js';
+import * as git from './git-util.js';
const exec = (cmd, args) => {
// Use `Observable` support if merged https://github.com/sindresorhus/execa/pull/26
const cp = execa(cmd, args);
- return merge(
- streamToObservable(cp.stdout.pipe(split())),
- streamToObservable(cp.stderr.pipe(split())),
- cp
- ).pipe(filter(Boolean));
+ return merge(cp.stdout, cp.stderr, cp).pipe(filter(Boolean));
};
-// eslint-disable-next-line default-param-last
-module.exports = async (input = 'patch', options) => {
+// eslint-disable-next-line complexity
+const np = async (input = 'patch', options) => {
if (!hasYarn() && options.yarn) {
throw new Error('Could not use Yarn without yarn.lock file');
}
@@ -47,14 +38,14 @@ module.exports = async (input = 'patch', options) => {
options.cleanup = false;
}
- const {pkg} = util.readPkg(options.contents);
+ const {pkg} = await util.readPkg(options.contents);
const runTests = options.tests && !options.yolo;
const runCleanup = options.cleanup && !options.yolo;
const pkgManager = options.yarn === true ? 'yarn' : 'npm';
const pkgManagerName = options.yarn === true ? 'Yarn' : 'npm';
- const rootDir = pkgDir.sync();
+ const rootDir = packageDirectorySync();
const hasLockFile = fs.existsSync(path.resolve(rootDir, options.yarn ? 'yarn.lock' : 'package-lock.json')) || fs.existsSync(path.resolve(rootDir, 'npm-shrinkwrap.json'));
- const isOnGitHub = options.repoUrl && (hostedGitInfo.fromUrl(options.repoUrl) || {}).type === 'github';
+ const isOnGitHub = options.repoUrl && hostedGitInfo.fromUrl(options.repoUrl)?.type === 'github';
const testScript = options.testScript || 'test';
const testCommand = options.testScript ? ['run', testScript] : [testScript];
@@ -75,8 +66,8 @@ module.exports = async (input = 'patch', options) => {
const versionInLatestTag = latestTag.slice(tagVersionPrefix.length);
try {
- if (versionInLatestTag === util.readPkg().pkg.version &&
- versionInLatestTag !== pkg.version) { // Verify that the package's version has been bumped before deleting the last tag and commit.
+ // Verify that the package's version has been bumped before deleting the last tag and commit.
+ if (versionInLatestTag === util.readPkg().version && versionInLatestTag !== pkg.version) {
await git.deleteTag(latestTag);
await git.removeLastCommit();
}
@@ -87,35 +78,31 @@ module.exports = async (input = 'patch', options) => {
}
});
- // The default parameter is a workaround for https://github.com/Tapppi/async-exit-hook/issues/9
- exitHook((callback = () => {}) => {
- if (options.preview) {
- callback();
- } else if (publishStatus === 'FAILED') {
- (async () => {
- await rollback();
- callback();
- })();
- } else if (publishStatus === 'SUCCESS') {
- callback();
+ asyncExitHook(async () => {
+ if (options.preview || publishStatus === 'SUCCESS') {
+ return;
+ }
+
+ if (publishStatus === 'FAILED') {
+ await rollback();
} else {
console.log('\nAborted!');
- callback();
}
- });
+ }, {minimumWait: 2000});
const tasks = new Listr([
{
title: 'Prerequisite check',
enabled: () => options.runPublish,
- task: () => prerequisiteTasks(input, pkg, options)
+ task: () => prerequisiteTasks(input, pkg, options),
},
{
title: 'Git',
- task: () => gitTasks(options)
- }
+ task: () => gitTasks(options),
+ },
], {
- showSubtasks: false
+ showSubtasks: false,
+ renderer: options.renderer ?? 'default',
});
if (runCleanup) {
@@ -123,13 +110,13 @@ module.exports = async (input = 'patch', options) => {
{
title: 'Cleanup',
enabled: () => !hasLockFile,
- task: () => del('node_modules')
+ task: () => deleteAsync('node_modules'),
},
{
title: 'Installing dependencies using Yarn',
enabled: () => options.yarn === true,
- task: () => {
- return exec('yarn', ['install', '--frozen-lockfile', '--production=false']).pipe(
+ task: () => (
+ exec('yarn', ['install', '--frozen-lockfile', '--production=false']).pipe(
catchError(async error => {
if ((!error.stderr.startsWith('error Your lockfile needs to be updated'))) {
return;
@@ -140,18 +127,18 @@ module.exports = async (input = 'patch', options) => {
}
throw new Error('yarn.lock file is outdated. Run yarn, commit the updated lockfile and try again.');
- })
- );
- }
+ }),
+ )
+ ),
},
{
title: 'Installing dependencies using npm',
enabled: () => options.yarn === false,
- task: () => {
+ task() {
const args = hasLockFile ? ['ci'] : ['install', '--no-package-lock', '--no-production'];
return exec('npm', [...args, '--engine-strict']);
- }
- }
+ },
+ },
]);
}
@@ -160,7 +147,7 @@ module.exports = async (input = 'patch', options) => {
{
title: 'Running tests using npm',
enabled: () => options.yarn === false,
- task: () => exec('npm', testCommand)
+ task: () => exec('npm', testCommand),
},
{
title: 'Running tests using Yarn',
@@ -171,10 +158,10 @@ module.exports = async (input = 'patch', options) => {
return [];
}
- return throwError(error);
- })
- )
- }
+ return throwError(() => error);
+ }),
+ ),
+ },
]);
}
@@ -182,7 +169,7 @@ module.exports = async (input = 'patch', options) => {
{
title: 'Bumping version using Yarn',
enabled: () => options.yarn === true,
- skip: () => {
+ skip() {
if (options.preview) {
let previewText = `[Preview] Command not executed: yarn version --new-version ${input}`;
@@ -193,7 +180,7 @@ module.exports = async (input = 'patch', options) => {
return `${previewText}.`;
}
},
- task: () => {
+ task() {
const args = ['version', '--new-version', input];
if (options.message) {
@@ -201,12 +188,12 @@ module.exports = async (input = 'patch', options) => {
}
return exec('yarn', args);
- }
+ },
},
{
title: 'Bumping version using npm',
enabled: () => options.yarn === false,
- skip: () => {
+ skip() {
if (options.preview) {
let previewText = `[Preview] Command not executed: npm version ${input}`;
@@ -217,7 +204,7 @@ module.exports = async (input = 'patch', options) => {
return `${previewText}.`;
}
},
- task: () => {
+ task() {
const args = ['version', input];
if (options.message) {
@@ -225,21 +212,21 @@ module.exports = async (input = 'patch', options) => {
}
return exec('npm', args);
- }
- }
+ },
+ },
]);
if (options.runPublish) {
tasks.add([
{
title: `Publishing package using ${pkgManagerName}`,
- skip: () => {
+ skip() {
if (options.preview) {
const args = publish.getPackagePublishArguments(options);
return `[Preview] Command not executed: ${pkgManager} ${args.join(' ')}.`;
}
},
- task: (context, task) => {
+ task(context, task) {
let hasError = false;
return publish(context, pkgManager, task, options)
@@ -251,10 +238,10 @@ module.exports = async (input = 'patch', options) => {
}),
finalize(() => {
publishStatus = hasError ? 'FAILED' : 'SUCCESS';
- })
+ }),
);
- }
- }
+ },
+ },
]);
const isExternalRegistry = npm.isExternalRegistry(pkg);
@@ -262,14 +249,14 @@ module.exports = async (input = 'patch', options) => {
tasks.add([
{
title: 'Enabling two-factor authentication',
- skip: () => {
+ skip() {
if (options.preview) {
const args = enable2fa.getEnable2faArgs(pkg.name, options);
return `[Preview] Command not executed: npm ${args.join(' ')}.`;
}
},
- task: (context, task) => enable2fa(task, pkg.name, {otp: context.otp})
- }
+ task: (context, task) => enable2fa(task, pkg.name, {otp: context.otp}),
+ },
]);
}
} else {
@@ -278,7 +265,7 @@ module.exports = async (input = 'patch', options) => {
tasks.add({
title: 'Pushing tags',
- skip: async () => {
+ async skip() {
if (!(await git.hasUpstream())) {
return 'Upstream branch not found; not pushing.';
}
@@ -291,21 +278,21 @@ module.exports = async (input = 'patch', options) => {
return 'Couldn\'t publish package to npm; not pushing.';
}
},
- task: async () => {
+ async task() {
pushedObjects = await git.pushGraceful(isOnGitHub);
- }
+ },
});
if (options.releaseDraft) {
tasks.add({
title: 'Creating release draft on GitHub',
enabled: () => isOnGitHub === true,
- skip: () => {
+ skip() {
if (options.preview) {
return '[Preview] GitHub Releases draft will not be opened in preview mode.';
}
},
- task: () => releaseTaskHelper(options, pkg)
+ task: () => releaseTaskHelper(options, pkg),
});
}
@@ -315,6 +302,8 @@ module.exports = async (input = 'patch', options) => {
console.error(`\n${logSymbols.error} ${pushedObjects.reason}`);
}
- const {packageJson: newPkg} = await readPkgUp();
+ const {packageJson: newPkg} = await readPackageUp();
return newPkg;
};
+
+export default np;
diff --git a/source/npm/enable-2fa.js b/source/npm/enable-2fa.js
index a9e2ef34..03c29ff5 100644
--- a/source/npm/enable-2fa.js
+++ b/source/npm/enable-2fa.js
@@ -1,10 +1,8 @@
-'use strict';
-const execa = require('execa');
-const {from} = require('rxjs');
-const {catchError} = require('rxjs/operators');
-const handleNpmError = require('./handle-npm-error');
+import {execa} from 'execa';
+import {from, catchError} from 'rxjs';
+import handleNpmError from './handle-npm-error.js';
-const getEnable2faArgs = (packageName, options) => {
+export const getEnable2faArgs = (packageName, options) => {
const args = ['access', '2fa-required', packageName];
if (options && options.otp) {
@@ -16,9 +14,10 @@ const getEnable2faArgs = (packageName, options) => {
const enable2fa = (packageName, options) => execa('npm', getEnable2faArgs(packageName, options));
-module.exports = (task, packageName, options) =>
+const tryEnable2fa = (task, packageName, options) => {
from(enable2fa(packageName, options)).pipe(
- catchError(error => handleNpmError(error, task, otp => enable2fa(packageName, {otp})))
+ catchError(error => handleNpmError(error, task, otp => enable2fa(packageName, {otp}))),
);
+};
-module.exports.getEnable2faArgs = getEnable2faArgs;
+export default tryEnable2fa;
diff --git a/source/npm/handle-npm-error.js b/source/npm/handle-npm-error.js
index fa191c51..7ec39c88 100644
--- a/source/npm/handle-npm-error.js
+++ b/source/npm/handle-npm-error.js
@@ -1,7 +1,6 @@
-const listrInput = require('listr-input');
-const chalk = require('chalk');
-const {throwError} = require('rxjs');
-const {catchError} = require('rxjs/operators');
+import listrInput from 'listr-input';
+import chalk from 'chalk';
+import {throwError, catchError} from 'rxjs';
const handleNpmError = (error, task, message, executor) => {
if (typeof message === 'function') {
@@ -15,13 +14,13 @@ const handleNpmError = (error, task, message, executor) => {
task.title = `${title} ${chalk.yellow('(waiting for input…)')}`;
return listrInput('Enter OTP:', {
- done: otp => {
+ done(otp) {
task.title = title;
return executor(otp);
},
- autoSubmit: value => value.length === 6
+ autoSubmit: value => value.length === 6,
}).pipe(
- catchError(error => handleNpmError(error, task, 'OTP was incorrect, try again:', executor))
+ catchError(error => handleNpmError(error, task, 'OTP was incorrect, try again:', executor)),
);
}
@@ -31,7 +30,7 @@ const handleNpmError = (error, task, message, executor) => {
throw new Error('You cannot publish a privately scoped package without a paid plan. Did you mean to publish publicly?');
}
- return throwError(error);
+ return throwError(() => error);
};
-module.exports = handleNpmError;
+export default handleNpmError;
diff --git a/source/npm/publish.js b/source/npm/publish.js
index 0a4ed4ee..85a73ddb 100644
--- a/source/npm/publish.js
+++ b/source/npm/publish.js
@@ -1,10 +1,8 @@
-'use strict';
-const execa = require('execa');
-const {from} = require('rxjs');
-const {catchError} = require('rxjs/operators');
-const handleNpmError = require('./handle-npm-error');
+import {execa} from 'execa';
+import {from, catchError} from 'rxjs';
+import handleNpmError from './handle-npm-error.js';
-const getPackagePublishArguments = options => {
+export const getPackagePublishArguments = options => {
const args = ['publish'];
if (options.contents) {
@@ -28,13 +26,14 @@ const getPackagePublishArguments = options => {
const pkgPublish = (pkgManager, options) => execa(pkgManager, getPackagePublishArguments(options));
-module.exports = (context, pkgManager, task, options) =>
+const publish = (context, pkgManager, task, options) => {
from(pkgPublish(pkgManager, options)).pipe(
catchError(error => handleNpmError(error, task, otp => {
context.otp = otp;
return pkgPublish(pkgManager, {...options, otp});
- }))
+ })),
);
+};
-module.exports.getPackagePublishArguments = getPackagePublishArguments;
+export default publish;
diff --git a/source/npm/util.js b/source/npm/util.js
index 2d594761..0d6582a5 100644
--- a/source/npm/util.js
+++ b/source/npm/util.js
@@ -1,16 +1,15 @@
-'use strict';
-const fs = require('fs');
-const path = require('path');
-const execa = require('execa');
-const pTimeout = require('p-timeout');
-const {default: ow} = require('ow');
-const npmName = require('npm-name');
-const chalk = require('chalk');
-const pkgDir = require('pkg-dir');
-const ignoreWalker = require('ignore-walk');
-const minimatch = require('minimatch');
-const {verifyRequirementSatisfied} = require('../version');
-const semver = require('semver');
+import fs from 'node:fs';
+import path from 'node:path';
+import {execa} from 'execa';
+import pTimeout from 'p-timeout';
+import ow from 'ow';
+import npmName from 'npm-name';
+import chalk from 'chalk';
+import {packageDirectorySync} from 'pkg-dir';
+import ignoreWalker from 'ignore-walk';
+import minimatch from 'minimatch';
+import semver from 'semver';
+import Version from '../version.js';
// According to https://docs.npmjs.com/files/package.json#files
// npm's default behavior is to ignore these files.
@@ -32,10 +31,10 @@ const filesIgnoredByDefault = [
'npm-debug.log',
'package-lock.json',
'.git/**/*',
- '.git'
+ '.git',
];
-exports.checkConnection = () => pTimeout(
+export const checkConnection = () => pTimeout(
(async () => {
try {
await execa('npm', ['ping']);
@@ -43,12 +42,13 @@ exports.checkConnection = () => pTimeout(
} catch {
throw new Error('Connection to npm registry failed');
}
- })(),
- 15000,
- 'Connection to npm registry timed out'
+ })(), {
+ milliseconds: 15_000,
+ message: 'Connection to npm registry timed out',
+ },
);
-exports.username = async ({externalRegistry}) => {
+export const username = async ({externalRegistry}) => {
const args = ['whoami'];
if (externalRegistry) {
@@ -59,19 +59,19 @@ exports.username = async ({externalRegistry}) => {
const {stdout} = await execa('npm', args);
return stdout;
} catch (error) {
- throw new Error(/ENEEDAUTH/.test(error.stderr) ?
- 'You must be logged in. Use `npm login` and try again.' :
- 'Authentication error. Use `npm whoami` to troubleshoot.');
+ throw new Error(/ENEEDAUTH/.test(error.stderr)
+ ? 'You must be logged in. Use `npm login` and try again.'
+ : 'Authentication error. Use `npm whoami` to troubleshoot.');
}
};
-exports.collaborators = async pkg => {
+export const collaborators = async pkg => {
const packageName = pkg.name;
ow(packageName, ow.string);
- const npmVersion = await exports.version();
+ const npmVersion = await version();
const args = semver.satisfies(npmVersion, '>=9.0.0') ? ['access', 'list', 'collaborators', packageName, '--json'] : ['access', 'ls-collaborators', packageName];
- if (exports.isExternalRegistry(pkg)) {
+ if (isExternalRegistry(pkg)) {
args.push('--registry', pkg.publishConfig.registry);
}
@@ -88,7 +88,7 @@ exports.collaborators = async pkg => {
}
};
-exports.prereleaseTags = async packageName => {
+export const prereleaseTags = async packageName => {
ow(packageName, ow.string);
let tags = [];
@@ -120,16 +120,16 @@ exports.prereleaseTags = async packageName => {
return tags;
};
-exports.isPackageNameAvailable = async pkg => {
+export const isPackageNameAvailable = async pkg => {
const args = [pkg.name];
const availability = {
isAvailable: false,
- isUnknown: false
+ isUnknown: false,
};
- if (exports.isExternalRegistry(pkg)) {
+ if (isExternalRegistry(pkg)) {
args.push({
- registryUrl: pkg.publishConfig.registry
+ registryUrl: pkg.publishConfig.registry,
});
}
@@ -142,19 +142,19 @@ exports.isPackageNameAvailable = async pkg => {
return availability;
};
-exports.isExternalRegistry = pkg => typeof pkg.publishConfig === 'object' && typeof pkg.publishConfig.registry === 'string';
+export const isExternalRegistry = pkg => typeof pkg.publishConfig === 'object' && typeof pkg.publishConfig.registry === 'string';
-exports.version = async () => {
+export const version = async () => {
const {stdout} = await execa('npm', ['--version']);
return stdout;
};
-exports.verifyRecentNpmVersion = async () => {
- const npmVersion = await exports.version();
- verifyRequirementSatisfied('npm', npmVersion);
+export const verifyRecentNpmVersion = async () => {
+ const npmVersion = await version();
+ Version.verifyRequirementSatisfied('npm', npmVersion);
};
-exports.checkIgnoreStrategy = ({files}) => {
+export const checkIgnoreStrategy = ({files}) => {
if (!files && !npmignoreExistsInPackageRootDir()) {
console.log(`
\n${chalk.bold.yellow('Warning:')} No ${chalk.bold.cyan('files')} field specified in ${chalk.bold.magenta('package.json')} nor is a ${chalk.bold.magenta('.npmignore')} file present. Having one of those will prevent you from accidentally publishing development-specific files along with your package's source code to npm.
@@ -163,7 +163,7 @@ exports.checkIgnoreStrategy = ({files}) => {
};
function npmignoreExistsInPackageRootDir() {
- const rootDir = pkgDir.sync();
+ const rootDir = packageDirectorySync();
return fs.existsSync(path.resolve(rootDir, '.npmignore'));
}
@@ -172,10 +172,11 @@ function excludeGitAndNodeModulesPaths(singlePath) {
}
async function getFilesIgnoredByDotnpmignore(pkg, fileList) {
- const allowList = (await ignoreWalker({
- path: pkgDir.sync(),
- ignoreFiles: ['.npmignore']
- })).filter(singlePath => excludeGitAndNodeModulesPaths(singlePath));
+ let allowList = await ignoreWalker({
+ path: packageDirectorySync(),
+ ignoreFiles: ['.npmignore'],
+ });
+ allowList = allowList.filter(singlePath => excludeGitAndNodeModulesPaths(singlePath));
return fileList.filter(minimatch.filter(getIgnoredFilesGlob(allowList, pkg.directories), {matchBase: true, dot: true}));
}
@@ -185,20 +186,20 @@ function filterFileList(globArray, fileList) {
}
const globString = globArray.length > 1 ? `{${globArray.filter(singlePath => excludeGitAndNodeModulesPaths(singlePath))}}` : globArray[0];
- return fileList.filter(minimatch.filter(globString, {matchBase: true, dot: true})); // eslint-disable-line unicorn/no-fn-reference-in-iterator
+ return fileList.filter(minimatch.filter(globString, {matchBase: true, dot: true})); // eslint-disable-line unicorn/no-array-callback-reference, unicorn/no-array-method-this-argument
}
async function getFilesIncludedByDotnpmignore(pkg, fileList) {
const allowList = await ignoreWalker({
- path: pkgDir.sync(),
- ignoreFiles: ['.npmignore']
+ path: packageDirectorySync(),
+ ignoreFiles: ['.npmignore'],
});
return filterFileList(allowList, fileList);
}
function getFilesNotIncludedInFilesProperty(pkg, fileList) {
const globArrayForFilesAndDirectories = [...pkg.files];
- const rootDir = pkgDir.sync();
+ const rootDir = packageDirectorySync();
for (const glob of pkg.files) {
try {
if (fs.statSync(path.resolve(rootDir, glob)).isDirectory()) {
@@ -213,7 +214,7 @@ function getFilesNotIncludedInFilesProperty(pkg, fileList) {
function getFilesIncludedInFilesProperty(pkg, fileList) {
const globArrayForFilesAndDirectories = [...pkg.files];
- const rootDir = pkgDir.sync();
+ const rootDir = packageDirectorySync();
for (const glob of pkg.files) {
try {
if (fs.statSync(path.resolve(rootDir, glob)).isDirectory()) {
@@ -236,7 +237,7 @@ function getDefaultIncludedFilesGlob(mainFile) {
'HISTORY*',
'LICENSE*',
'LICENCE*',
- 'NOTICE*'
+ 'NOTICE*',
];
if (mainFile) {
filesAlwaysIncluded.push(mainFile);
@@ -261,7 +262,7 @@ function getIgnoredFilesGlob(globArrayFromFilesProperty, packageDirectories) {
}
// Get all files which will be ignored by either `.npmignore` or the `files` property in `package.json` (if defined).
-exports.getNewAndUnpublishedFiles = async (pkg, newFiles = []) => {
+export const getNewAndUnpublishedFiles = async (pkg, newFiles = []) => {
if (pkg.files) {
return getFilesNotIncludedInFilesProperty(pkg, newFiles);
}
@@ -273,7 +274,7 @@ exports.getNewAndUnpublishedFiles = async (pkg, newFiles = []) => {
return [];
};
-exports.getFirstTimePublishedFiles = async (pkg, newFiles = []) => {
+export const getFirstTimePublishedFiles = async (pkg, newFiles = []) => {
let result;
if (pkg.files) {
result = getFilesIncludedInFilesProperty(pkg, newFiles);
@@ -286,11 +287,10 @@ exports.getFirstTimePublishedFiles = async (pkg, newFiles = []) => {
return result.filter(minimatch.filter(`!{${filesIgnoredByDefault}}`, {matchBase: true, dot: true})).filter(minimatch.filter(getDefaultIncludedFilesGlob(pkg.main), {nocase: true, matchBase: true}));
};
-exports.getRegistryUrl = async (pkgManager, pkg) => {
+export const getRegistryUrl = async (pkgManager, pkg) => {
const args = ['config', 'get', 'registry'];
- if (exports.isExternalRegistry(pkg)) {
- args.push('--registry');
- args.push(pkg.publishConfig.registry);
+ if (isExternalRegistry(pkg)) {
+ args.push('--registry', pkg.publishConfig.registry);
}
const {stdout} = await execa(pkgManager, args);
diff --git a/source/prerequisite-tasks.js b/source/prerequisite-tasks.js
index 56b12aa7..50006c8d 100644
--- a/source/prerequisite-tasks.js
+++ b/source/prerequisite-tasks.js
@@ -1,12 +1,12 @@
-'use strict';
-const Listr = require('listr');
-const execa = require('execa');
-const version = require('./version');
-const git = require('./git-util');
-const npm = require('./npm/util');
-const {getTagVersionPrefix} = require('./util');
+import process from 'node:process';
+import Listr from 'listr';
+import {execa} from 'execa';
+import Version from './version.js';
+import * as git from './git-util.js';
+import * as npm from './npm/util.js';
+import {getTagVersionPrefix} from './util.js';
-module.exports = (input, pkg, options) => {
+const prerequisiteTasks = (input, pkg, options) => {
const isExternalRegistry = npm.isExternalRegistry(pkg);
let newVersion = null;
@@ -14,26 +14,26 @@ module.exports = (input, pkg, options) => {
{
title: 'Ping npm registry',
enabled: () => !pkg.private && !isExternalRegistry,
- task: async () => npm.checkConnection()
+ task: async () => npm.checkConnection(),
},
{
title: 'Check npm version',
- task: async () => npm.verifyRecentNpmVersion()
+ task: async () => npm.verifyRecentNpmVersion(),
},
{
title: 'Check yarn version',
enabled: () => options.yarn === true,
- task: async () => {
+ async task() {
const {stdout: yarnVersion} = await execa('yarn', ['--version']);
- version.verifyRequirementSatisfied('yarn', yarnVersion);
- }
+ Version.verifyRequirementSatisfied('yarn', yarnVersion);
+ },
},
{
title: 'Verify user is authenticated',
enabled: () => process.env.NODE_ENV !== 'test' && !pkg.private,
- task: async () => {
+ async task() {
const username = await npm.username({
- externalRegistry: isExternalRegistry ? pkg.publishConfig.registry : false
+ externalRegistry: isExternalRegistry ? pkg.publishConfig.registry : false,
});
const collaborators = await npm.collaborators(pkg);
@@ -46,41 +46,43 @@ module.exports = (input, pkg, options) => {
if (!permissions || !permissions.includes('write')) {
throw new Error('You do not have write permissions required to publish this package.');
}
- }
+ },
},
{
title: 'Check git version',
- task: async () => git.verifyRecentGitVersion()
+ task: async () => git.verifyRecentGitVersion(),
},
{
title: 'Check git remote',
- task: async () => git.verifyRemoteIsValid()
+ task: async () => git.verifyRemoteIsValid(),
},
{
title: 'Validate version',
- task: () => {
- newVersion = version.getAndValidateNewVersionFrom(input, pkg.version);
- }
+ task() {
+ newVersion = Version.getAndValidateNewVersionFrom(input, pkg.version);
+ },
},
{
title: 'Check for pre-release version',
- task: () => {
- if (!pkg.private && version(newVersion).isPrerelease() && !options.tag) {
+ task() {
+ if (!pkg.private && new Version(newVersion).isPrerelease() && !options.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 git tag existence',
- task: async () => {
+ async task() {
await git.fetch();
const tagPrefix = await getTagVersionPrefix(options);
await git.verifyTagDoesNotExistOnRemote(`${tagPrefix}${newVersion}`);
- }
- }
+ },
+ },
];
return new Listr(tasks);
};
+
+export default prerequisiteTasks;
diff --git a/source/pretty-version-diff.js b/source/pretty-version-diff.js
index 15eb60b8..059fa219 100644
--- a/source/pretty-version-diff.js
+++ b/source/pretty-version-diff.js
@@ -1,9 +1,8 @@
-'use strict';
-const chalk = require('chalk');
-const version = require('./version');
+import chalk from 'chalk';
+import Version from './version.js';
-module.exports = (oldVersion, inc) => {
- const newVersion = version(oldVersion).getNewVersionFrom(inc).split('.');
+const prettyVersionDiff = (oldVersion, inc) => {
+ const newVersion = new Version(oldVersion).getNewVersionFrom(inc).split('.');
oldVersion = oldVersion.split('.');
let firstVersionChange = false;
const output = [];
@@ -23,3 +22,5 @@ module.exports = (oldVersion, inc) => {
return output.join(chalk.reset.dim('.'));
};
+
+export default prettyVersionDiff;
diff --git a/source/release-task-helper.js b/source/release-task-helper.js
index 53d93d6f..2e71aca9 100644
--- a/source/release-task-helper.js
+++ b/source/release-task-helper.js
@@ -1,13 +1,12 @@
-'use strict';
-const open = require('open');
-const newGithubReleaseUrl = require('new-github-release-url');
-const {getTagVersionPrefix, getPreReleasePrefix} = require('./util');
-const version = require('./version');
+import open from 'open';
+import newGithubReleaseUrl from 'new-github-release-url';
+import {getTagVersionPrefix, getPreReleasePrefix} from './util.js';
+import Version from './version.js';
-module.exports = async (options, pkg) => {
- const newVersion = version(pkg.version).getNewVersionFrom(options.version);
+const releaseTaskHelper = async (options, pkg) => {
+ const newVersion = new Version(pkg.version).getNewVersionFrom(options.version);
let tag = await getTagVersionPrefix(options) + newVersion;
- const isPreRelease = version(options.version).isPrerelease();
+ const isPreRelease = new Version(options.version).isPrerelease();
if (isPreRelease) {
tag += await getPreReleasePrefix(options);
}
@@ -16,8 +15,10 @@ module.exports = async (options, pkg) => {
repoUrl: options.repoUrl,
tag,
body: options.releaseNotes(tag),
- isPrerelease: isPreRelease
+ isPrerelease: isPreRelease,
});
await open(url);
};
+
+export default releaseTaskHelper;
diff --git a/source/ui.js b/source/ui.js
index 247cbd39..3cbfb213 100644
--- a/source/ui.js
+++ b/source/ui.js
@@ -1,15 +1,14 @@
-'use strict';
-const inquirer = require('inquirer');
-const chalk = require('chalk');
-const githubUrlFromGit = require('github-url-from-git');
-const {htmlEscape} = require('escape-goat');
-const isScoped = require('is-scoped');
-const isInteractive = require('is-interactive');
-const util = require('./util');
-const git = require('./git-util');
-const {prereleaseTags, checkIgnoreStrategy, getRegistryUrl, isExternalRegistry} = require('./npm/util');
-const version = require('./version');
-const prettyVersionDiff = require('./pretty-version-diff');
+import inquirer from 'inquirer';
+import chalk from 'chalk';
+import githubUrlFromGit from 'github-url-from-git';
+import {htmlEscape} from 'escape-goat';
+import isScoped from 'is-scoped';
+import isInteractive from 'is-interactive';
+import * as util from './util.js';
+import * as git from './git-util.js';
+import {prereleaseTags, checkIgnoreStrategy, getRegistryUrl, isExternalRegistry} from './npm/util.js';
+import Version from './version.js';
+import prettyVersionDiff from './pretty-version-diff.js';
const printCommitLog = async (repoUrl, registryUrl, fromLatestTag, releaseBranch) => {
const revision = fromLatestTag ? await git.latestTagOrFirstCommit() : await git.previousTagOrFirstCommit();
@@ -23,7 +22,7 @@ const printCommitLog = async (repoUrl, registryUrl, fromLatestTag, releaseBranch
return {
hasCommits: false,
hasUnreleasedCommits: false,
- releaseNotes: () => {}
+ releaseNotes() {},
};
}
@@ -35,7 +34,7 @@ const printCommitLog = async (repoUrl, registryUrl, fromLatestTag, releaseBranch
const splitIndex = commit.lastIndexOf(' ');
return {
message: commit.slice(0, splitIndex),
- id: commit.slice(splitIndex + 1)
+ id: commit.slice(splitIndex + 1),
};
});
@@ -66,7 +65,7 @@ const printCommitLog = async (repoUrl, registryUrl, fromLatestTag, releaseBranch
}).join('\n');
const releaseNotes = nextTag => commits.map(commit =>
- `- ${htmlEscape(commit.message)} ${commit.id}`
+ `- ${htmlEscape(commit.message)} ${commit.id}`,
).join('\n') + `\n\n${repoUrl}/compare/${revision}...${nextTag}`;
const commitRange = util.linkifyCommitRange(repoUrl, commitRangeText);
@@ -75,7 +74,7 @@ const printCommitLog = async (repoUrl, registryUrl, fromLatestTag, releaseBranch
return {
hasCommits: true,
hasUnreleasedCommits,
- releaseNotes
+ releaseNotes,
};
};
@@ -115,13 +114,14 @@ const checkNewFilesAndDependencies = async (pkg, pkgPath) => {
type: 'confirm',
name: 'confirm',
message: `${messages.join('\n')}\nContinue?`,
- default: false
+ default: false,
}]);
return answers.confirm;
};
-module.exports = async (options, {pkg, pkgPath}) => {
+// eslint-disable-next-line complexity
+const ui = async (options, {pkg, pkgPath}) => {
const oldVersion = pkg.version;
const extraBaseUrls = ['gitlab.com'];
const repoUrl = pkg.repository && githubUrlFromGit(pkg.repository.url, {extraBaseUrls});
@@ -136,7 +136,7 @@ module.exports = async (options, {pkg, pkgPath}) => {
if (!answerIgnoredFiles) {
return {
...options,
- confirm: answerIgnoredFiles
+ confirm: answerIgnoredFiles,
};
}
}
@@ -144,56 +144,115 @@ module.exports = async (options, {pkg, pkgPath}) => {
if (options.releaseDraftOnly) {
console.log(`\nCreate a release draft on GitHub for ${chalk.bold.magenta(pkg.name)} ${chalk.dim(`(current: ${oldVersion})`)}\n`);
} else {
- const newVersion = options.version ? version.getAndValidateNewVersionFrom(options.version, oldVersion) : undefined;
+ const newVersion = options.version ? Version.getAndValidateNewVersionFrom(options.version, oldVersion) : undefined;
const versionText = chalk.dim(`(current: ${oldVersion}${newVersion ? `, next: ${prettyVersionDiff(oldVersion, newVersion)}` : ''}${chalk.dim(')')}`);
console.log(`\nPublish a new version of ${chalk.bold.magenta(pkg.name)} ${versionText}\n`);
}
- const prompts = [
- {
+ const useLatestTag = !options.releaseDraftOnly;
+ const {hasCommits, hasUnreleasedCommits, releaseNotes} = await printCommitLog(repoUrl, registryUrl, useLatestTag, releaseBranch);
+
+ if (hasUnreleasedCommits && options.releaseDraftOnly) {
+ const answers = await inquirer.prompt({
+ confirm: {
+ type: 'confirm',
+ message: 'Unreleased commits found. They won\'t be included in the release draft. Continue?',
+ default: false,
+ },
+ });
+
+ if (!answers.confirm) {
+ return {
+ ...options,
+ ...answers,
+ };
+ }
+ }
+
+ if (options.version) {
+ return {
+ ...options,
+ confirm: true,
+ repoUrl,
+ releaseNotes,
+ };
+ }
+
+ if (!hasCommits) {
+ const answers = await inquirer.prompt({
+ confirm: {
+ type: 'confirm',
+ message: 'No commits found since previous release, continue?',
+ default: false,
+ },
+ });
+
+ if (!answers.confirm) {
+ return {
+ ...options,
+ ...answers,
+ };
+ }
+ }
+
+ if (options.availability.isUnknown) {
+ const answers = await inquirer.prompt({
+ confirm: {
+ type: 'confirm',
+ when: isScoped(pkg.name) && options.runPublish,
+ message: `Failed to check availability of scoped repo name ${chalk.bold.magenta(pkg.name)}. Do you want to try and publish it anyway?`,
+ default: false,
+ },
+ });
+
+ if (!answers.confirm) {
+ return {
+ ...options,
+ ...answers,
+ };
+ }
+ }
+
+ const answers = await inquirer.prompt({
+ version: {
type: 'list',
- name: 'version',
message: 'Select semver increment or specify new version',
- pageSize: version.SEMVER_INCREMENTS.length + 2,
- choices: version.SEMVER_INCREMENTS
+ pageSize: Version.SEMVER_INCREMENTS.length + 2,
+ choices: [...Version.SEMVER_INCREMENTS
.map(inc => ({
name: `${inc} ${prettyVersionDiff(oldVersion, inc)}`,
- value: inc
- }))
- .concat([
- new inquirer.Separator(),
- {
- name: 'Other (specify)',
- value: null
- }
- ]),
- filter: input => version.isValidInput(input) ? version(oldVersion).getNewVersionFrom(input) : input
+ value: inc,
+ })),
+ new inquirer.Separator(),
+ {
+ name: 'Other (specify)',
+ value: null,
+ }],
+ filter: input => Version.isValidInput(input) ? new Version(oldVersion).getNewVersionFrom(input) : input,
},
- {
+ customVersion: {
type: 'input',
- name: 'customVersion',
message: 'Version',
when: answers => !answers.version,
- filter: input => version.isValidInput(input) ? version(pkg.version).getNewVersionFrom(input) : input,
- validate: input => {
- if (!version.isValidInput(input)) {
+ filter: input => Version.isValidInput(input) ? new Version(pkg.version).getNewVersionFrom(input) : input,
+ validate(input) {
+ if (!Version.isValidInput(input)) {
return 'Please specify a valid semver, for example, `1.2.3`. See https://semver.org';
}
- if (version(oldVersion).isLowerThanOrEqualTo(input)) {
+ if (new Version(oldVersion).isLowerThanOrEqualTo(input)) {
return `Version must be greater than ${oldVersion}`;
}
return true;
- }
+ },
},
- {
+ tag: {
type: 'list',
- name: 'tag',
message: 'How should this pre-release version be tagged in npm?',
- when: answers => options.runPublish && (version.isPrereleaseOrIncrement(answers.customVersion) || version.isPrereleaseOrIncrement(answers.version)) && !options.tag,
- choices: async () => {
+ when: answers => options.runPublish && (Version.isPrereleaseOrIncrement(answers.customVersion) || Version.isPrereleaseOrIncrement(answers.version)) && !options.tag,
+ async choices() {
const existingPrereleaseTags = await prereleaseTags(pkg.name);
return [
@@ -201,17 +260,16 @@ module.exports = async (options, {pkg, pkgPath}) => {
new inquirer.Separator(),
{
name: 'Other (specify)',
- value: null
- }
+ value: null,
+ },
];
- }
+ },
},
- {
+ customTag: {
type: 'input',
- name: 'customTag',
message: 'Tag',
- when: answers => options.runPublish && (version.isPrereleaseOrIncrement(answers.customVersion) || version.isPrereleaseOrIncrement(answers.version)) && !options.tag && !answers.tag,
- validate: input => {
+ when: answers => options.runPublish && (Version.isPrereleaseOrIncrement(answers.customVersion) || Version.isPrereleaseOrIncrement(answers.version)) && !options.tag && !answers.tag,
+ validate(input) {
if (input.length === 0) {
return 'Please specify a tag, for example, `next`.';
}
@@ -221,79 +279,15 @@ module.exports = async (options, {pkg, pkgPath}) => {
}
return true;
- }
+ },
},
- {
+ publishScoped: {
type: 'confirm',
- name: 'publishScoped',
when: isScoped(pkg.name) && options.availability.isAvailable && !options.availability.isUnknown && options.runPublish && (pkg.publishConfig && pkg.publishConfig.access !== 'restricted') && !isExternalRegistry(pkg),
message: `This scoped repo ${chalk.bold.magenta(pkg.name)} hasn't been published. Do you want to publish it publicly?`,
- default: false
- }
- ];
-
- const useLatestTag = !options.releaseDraftOnly;
- const {hasCommits, hasUnreleasedCommits, releaseNotes} = await printCommitLog(repoUrl, registryUrl, useLatestTag, releaseBranch);
-
- if (hasUnreleasedCommits && options.releaseDraftOnly) {
- const answers = await inquirer.prompt([{
- type: 'confirm',
- name: 'confirm',
- message: 'Unreleased commits found. They won\'t be included in the release draft. Continue?',
- default: false
- }]);
-
- if (!answers.confirm) {
- return {
- ...options,
- ...answers
- };
- }
- }
-
- if (options.version) {
- return {
- ...options,
- confirm: true,
- repoUrl,
- releaseNotes
- };
- }
-
- if (!hasCommits) {
- const answers = await inquirer.prompt([{
- type: 'confirm',
- name: 'confirm',
- message: 'No commits found since previous release, continue?',
- default: false
- }]);
-
- if (!answers.confirm) {
- return {
- ...options,
- ...answers
- };
- }
- }
-
- if (options.availability.isUnknown) {
- const answers = await inquirer.prompt([{
- type: 'confirm',
- name: 'confirm',
- when: isScoped(pkg.name) && options.runPublish,
- message: `Failed to check availability of scoped repo name ${chalk.bold.magenta(pkg.name)}. Do you want to try and publish it anyway?`,
- default: false
- }]);
-
- if (!answers.confirm) {
- return {
- ...options,
- ...answers
- };
- }
- }
-
- const answers = await inquirer.prompt(prompts);
+ default: false,
+ },
+ });
return {
...options,
@@ -302,6 +296,8 @@ module.exports = async (options, {pkg, pkgPath}) => {
publishScoped: answers.publishScoped,
confirm: true,
repoUrl,
- releaseNotes
+ releaseNotes,
};
};
+
+export default ui;
diff --git a/source/util.js b/source/util.js
index 0b683fc6..b76a4080 100644
--- a/source/util.js
+++ b/source/util.js
@@ -1,30 +1,28 @@
-'use strict';
-const readPkgUp = require('read-pkg-up');
-const issueRegex = require('issue-regex');
-const terminalLink = require('terminal-link');
-const execa = require('execa');
-const pMemoize = require('p-memoize');
-const {default: ow} = require('ow');
-const pkgDir = require('pkg-dir');
-const chalk = require('chalk');
-const gitUtil = require('./git-util');
-const npmUtil = require('./npm/util');
-
-exports.readPkg = packagePath => {
- packagePath = packagePath ? pkgDir.sync(packagePath) : pkgDir.sync();
-
+import {readPackageUp} from 'read-pkg-up';
+import issueRegex from 'issue-regex';
+import terminalLink from 'terminal-link';
+import {execa} from 'execa';
+import pMemoize from 'p-memoize';
+import ow from 'ow';
+import chalk from 'chalk';
+import {packageDirectory} from 'pkg-dir';
+import * as gitUtil from './git-util.js';
+import * as npmUtil from './npm/util.js';
+
+export const readPkg = async packagePath => {
+ packagePath = packagePath ? await packageDirectory(packagePath) : await packageDirectory();
if (!packagePath) {
throw new Error('No `package.json` found. Make sure the current directory is a valid package.');
}
- const {packageJson, path} = readPkgUp.sync({
- cwd: packagePath
+ const {packageJson, path} = await readPackageUp({
+ cwd: packagePath,
});
return {pkg: packageJson, pkgPath: path};
};
-exports.linkifyIssues = (url, message) => {
+export const linkifyIssues = (url, message) => {
if (!(url && terminalLink.isSupported)) {
return message;
}
@@ -40,7 +38,7 @@ exports.linkifyIssues = (url, message) => {
});
};
-exports.linkifyCommit = (url, commit) => {
+export const linkifyCommit = (url, commit) => {
if (!(url && terminalLink.isSupported)) {
return commit;
}
@@ -48,7 +46,7 @@ exports.linkifyCommit = (url, commit) => {
return terminalLink(commit, `${url}/commit/${commit}`);
};
-exports.linkifyCommitRange = (url, commitRange) => {
+export const linkifyCommitRange = (url, commitRange) => {
if (!(url && terminalLink.isSupported)) {
return commitRange;
}
@@ -56,7 +54,7 @@ exports.linkifyCommitRange = (url, commitRange) => {
return terminalLink(commitRange, `${url}/compare/${commitRange}`);
};
-exports.getTagVersionPrefix = pMemoize(async options => {
+export const getTagVersionPrefix = pMemoize(async options => {
ow(options, ow.object.hasKeys('yarn'));
try {
@@ -72,14 +70,14 @@ exports.getTagVersionPrefix = pMemoize(async options => {
}
});
-exports.joinList = list => chalk.reset(list.map(item => `- ${item}`).join('\n'));
+export const joinList = list => chalk.reset(list.map(item => `- ${item}`).join('\n'));
-exports.getNewFiles = async pkg => {
+export const getNewFiles = async pkg => {
const listNewFiles = await gitUtil.newFilesSinceLastRelease();
return {unpublished: await npmUtil.getNewAndUnpublishedFiles(pkg, listNewFiles), firstTime: await npmUtil.getFirstTimePublishedFiles(pkg, listNewFiles)};
};
-exports.getNewDependencies = async (newPkg, pkgPath) => {
+export const getNewDependencies = async (newPkg, pkgPath) => {
let oldPkg = await gitUtil.readFileFromLastRelease(pkgPath);
oldPkg = JSON.parse(oldPkg);
@@ -94,7 +92,7 @@ exports.getNewDependencies = async (newPkg, pkgPath) => {
return newDependencies;
};
-exports.getPreReleasePrefix = pMemoize(async options => {
+export const getPreReleasePrefix = pMemoize(async options => {
ow(options, ow.object.hasKeys('yarn'));
try {
diff --git a/source/version.js b/source/version.js
index a0504466..56710fac 100644
--- a/source/version.js
+++ b/source/version.js
@@ -1,7 +1,9 @@
-'use strict';
-const semver = require('semver');
+import semver from 'semver';
+import {readPackageUp} from 'read-pkg-up';
-class Version {
+const {packageJson: pkg} = await readPackageUp();
+
+export default class Version {
constructor(version) {
this.version = version;
}
@@ -11,66 +13,64 @@ class Version {
}
satisfies(range) {
- module.exports.validate(this.version);
+ Version.validate(this.version);
return semver.satisfies(this.version, range, {
- includePrerelease: true
+ includePrerelease: true,
});
}
getNewVersionFrom(input) {
- module.exports.validate(this.version);
- if (!module.exports.isValidInput(input)) {
- throw new Error(`Version should be either ${module.exports.SEMVER_INCREMENTS.join(', ')}, or a valid semver version.`);
+ Version.validate(this.version);
+ if (!Version.isValidInput(input)) {
+ throw new Error(`Version should be either ${Version.SEMVER_INCREMENTS.join(', ')}, or a valid semver version.`);
}
- return module.exports.SEMVER_INCREMENTS.includes(input) ? semver.inc(this.version, input) : input;
+ return Version.SEMVER_INCREMENTS.includes(input) ? semver.inc(this.version, input) : input;
}
isGreaterThanOrEqualTo(otherVersion) {
- module.exports.validate(this.version);
- module.exports.validate(otherVersion);
+ Version.validate(this.version);
+ Version.validate(otherVersion);
return semver.gte(otherVersion, this.version);
}
isLowerThanOrEqualTo(otherVersion) {
- module.exports.validate(this.version);
- module.exports.validate(otherVersion);
+ Version.validate(this.version);
+ Version.validate(otherVersion);
return semver.lte(otherVersion, this.version);
}
-}
-
-module.exports = version => new Version(version);
-module.exports.SEMVER_INCREMENTS = ['patch', 'minor', 'major', 'prepatch', 'preminor', 'premajor', 'prerelease'];
-module.exports.PRERELEASE_VERSIONS = ['prepatch', 'preminor', 'premajor', 'prerelease'];
+ static SEMVER_INCREMENTS = ['patch', 'minor', 'major', 'prepatch', 'preminor', 'premajor', 'prerelease'];
+ static PRERELEASE_VERSIONS = ['prepatch', 'preminor', 'premajor', 'prerelease'];
-module.exports.isPrereleaseOrIncrement = input => module.exports(input).isPrerelease() || module.exports.PRERELEASE_VERSIONS.includes(input);
+ static isPrereleaseOrIncrement = input => new Version(input).isPrerelease() || Version.PRERELEASE_VERSIONS.includes(input);
-const isValidVersion = input => Boolean(semver.valid(input));
+ static isValidVersion = input => Boolean(semver.valid(input));
-module.exports.isValidInput = input => module.exports.SEMVER_INCREMENTS.includes(input) || isValidVersion(input);
+ static isValidInput = input => Version.SEMVER_INCREMENTS.includes(input) || Version.isValidVersion(input);
-module.exports.validate = version => {
- if (!isValidVersion(version)) {
- throw new Error('Version should be a valid semver version.');
+ static validate(version) {
+ if (!Version.isValidVersion(version)) {
+ throw new Error('Version should be a valid semver version.');
+ }
}
-};
-module.exports.verifyRequirementSatisfied = (dependency, version) => {
- const depRange = require('../package.json').engines[dependency];
- if (!module.exports(version).satisfies(depRange)) {
- throw new Error(`Please upgrade to ${dependency}${depRange}`);
+ static verifyRequirementSatisfied(dependency, version) {
+ const depRange = pkg.engines[dependency];
+ if (!new Version(version).satisfies(depRange)) {
+ throw new Error(`Please upgrade to ${dependency}${depRange}`);
+ }
}
-};
-module.exports.getAndValidateNewVersionFrom = (input, version) => {
- const newVersion = module.exports(version).getNewVersionFrom(input);
+ static getAndValidateNewVersionFrom(input, version) {
+ const newVersion = new Version(version).getNewVersionFrom(input);
- if (module.exports(version).isLowerThanOrEqualTo(newVersion)) {
- throw new Error(`New version \`${newVersion}\` should be higher than current version \`${version}\``);
- }
+ if (new Version(version).isLowerThanOrEqualTo(newVersion)) {
+ throw new Error(`New version \`${newVersion}\` should be higher than current version \`${version}\``);
+ }
- return newVersion;
-};
+ return newVersion;
+ }
+}
diff --git a/test/_utils.js b/test/_utils.js
new file mode 100644
index 00000000..1f9a47f3
--- /dev/null
+++ b/test/_utils.js
@@ -0,0 +1,50 @@
+import esmock from 'esmock';
+import {execa} from 'execa';
+import {SilentRenderer} from './fixtures/listr-renderer.js';
+
+export const _stubExeca = source => async (t, commands) => esmock(source, {}, {
+ execa: {
+ async execa(...args) {
+ const results = await Promise.all(commands.map(async result => {
+ const argsMatch = await t.try(tt => {
+ const [command, ...commandArgs] = result.command.split(' ');
+ tt.deepEqual(args, [command, commandArgs]);
+ });
+
+ if (argsMatch.passed) {
+ argsMatch.discard();
+
+ if (!result.exitCode || result.exitCode === 0) {
+ return result;
+ }
+
+ throw result;
+ }
+
+ argsMatch.discard();
+ }));
+
+ const result = results.filter(Boolean).at(0);
+ return result ?? execa(...args);
+ },
+ },
+});
+
+export const run = async listr => {
+ listr.setRenderer(SilentRenderer);
+ await listr.run();
+};
+
+export const assertTaskFailed = (t, taskTitle) => {
+ const task = SilentRenderer.tasks.find(task => task.title === taskTitle);
+ t.true(task.hasFailed(), `'${taskTitle}' did not fail!`);
+};
+
+export const assertTaskDisabled = (t, taskTitle) => {
+ const task = SilentRenderer.tasks.find(task => task.title === taskTitle);
+ t.true(!task.isEnabled(), `'${taskTitle}' was enabled!`);
+};
+
+export const assertTaskDoesntExist = (t, taskTitle) => {
+ t.true(SilentRenderer.tasks.every(task => task.title !== taskTitle), `'${taskTitle}' exists!`);
+};
diff --git a/test/config.js b/test/config.js
index 3c577f69..0800c14d 100644
--- a/test/config.js
+++ b/test/config.js
@@ -1,90 +1,100 @@
-import path from 'path';
+import path from 'node:path';
import test from 'ava';
import sinon from 'sinon';
-import proxyquire from 'proxyquire';
+import esmock from 'esmock';
-const fixtureBasePath = path.resolve('test', 'fixtures', 'config');
+const testedModulePath = '../source/config.js';
+
+const getFixture = fixture => path.resolve('test', 'fixtures', 'config', fixture);
+const getFixtures = fixtures => fixtures.map(fixture => getFixture(fixture));
const getConfigsWhenGlobalBinaryIsUsed = async homedirStub => {
- const pathsPkgDir = [path.resolve(fixtureBasePath, 'pkg-dir'),
- path.resolve(fixtureBasePath, 'local1'),
- path.resolve(fixtureBasePath, 'local2'),
- path.resolve(fixtureBasePath, 'local3')];
-
- const promises = [];
- pathsPkgDir.forEach(pathPkgDir => {
- promises.push(proxyquire('../source/config', {
+ const pathsPkgDir = getFixtures(['pkg-dir', 'local1', 'local2', 'local3']);
+
+ const promises = pathsPkgDir.map(async pathPkgDir => {
+ const getConfig = await esmock(testedModulePath, {
'is-installed-globally': true,
- 'pkg-dir': async () => {
- return pathPkgDir;
- },
- os: {
- homedir: homedirStub
- }
- })());
+ 'pkg-dir': {packageDirectory: async () => pathPkgDir},
+ 'node:os': {homedir: homedirStub},
+ });
+ return getConfig();
});
+
return Promise.all(promises);
};
const getConfigsWhenLocalBinaryIsUsed = async pathPkgDir => {
- const homedirs = [path.resolve(fixtureBasePath, 'homedir1'),
- path.resolve(fixtureBasePath, 'homedir2'),
- path.resolve(fixtureBasePath, 'homedir3')];
+ const homedirs = getFixtures(['homedir1', 'homedir2', 'homedir3']);
- const promises = [];
- homedirs.forEach(homedir => {
- promises.push(proxyquire('../source/config', {
+ const promises = homedirs.map(async homedir => {
+ const getConfig = await esmock(testedModulePath, {
'is-installed-globally': false,
- 'pkg-dir': async () => {
- return pathPkgDir;
- },
- os: {
- homedir: () => {
- return homedir;
- }
- }
- })());
+ 'pkg-dir': {packageDirectory: async () => pathPkgDir},
+ 'node:os': {homedir: () => homedir},
+ });
+ return getConfig();
});
+
return Promise.all(promises);
};
-test('returns config from home directory when global binary is used and `.np-config-json` exists in home directory', async t => {
- const homedirStub = sinon.stub();
- homedirStub.returns(path.resolve(fixtureBasePath, 'homedir1'));
+const useGlobalBinary = test.macro(async (t, homedir, source) => {
+ const homedirStub = sinon.stub().returns(getFixture(homedir));
const configs = await getConfigsWhenGlobalBinaryIsUsed(homedirStub);
- configs.forEach(config => t.deepEqual(config, {source: 'homedir/.np-config.json'}));
-});
-test('returns config from home directory when global binary is used and `.np-config.js` exists in home directory', async t => {
- const homedirStub = sinon.stub();
- homedirStub.returns(path.resolve(fixtureBasePath, 'homedir2'));
- const configs = await getConfigsWhenGlobalBinaryIsUsed(homedirStub);
- configs.forEach(config => t.deepEqual(config, {source: 'homedir/.np-config.js'}));
+ for (const config of configs) {
+ t.deepEqual(config, {source});
+ }
});
-test('returns config from home directory when global binary is used and `.np-config.cjs` exists in home directory', async t => {
- const homedirStub = sinon.stub();
- homedirStub.returns(path.resolve(fixtureBasePath, 'homedir3'));
- const configs = await getConfigsWhenGlobalBinaryIsUsed(homedirStub);
- configs.forEach(config => t.deepEqual(config, {source: 'homedir/.np-config.cjs'}));
-});
+const useLocalBinary = test.macro(async (t, pkgDir, source) => {
+ const configs = await getConfigsWhenLocalBinaryIsUsed(getFixture(pkgDir));
-test('returns config from package directory when local binary is used and `package.json` exists in package directory', async t => {
- const configs = await getConfigsWhenLocalBinaryIsUsed(path.resolve(fixtureBasePath, 'pkg-dir'));
- configs.forEach(config => t.deepEqual(config, {source: 'package.json'}));
+ for (const config of configs) {
+ t.deepEqual(config, {source});
+ }
});
-test('returns config from package directory when local binary is used and `.np-config.json` exists in package directory', async t => {
- const configs = await getConfigsWhenLocalBinaryIsUsed(path.resolve(fixtureBasePath, 'local1'));
- configs.forEach(config => t.deepEqual(config, {source: 'packagedir/.np-config.json'}));
-});
+test('returns config from home directory when global binary is used and .np-config-json exists in home directory',
+ useGlobalBinary, 'homedir1', 'homedir/.np-config.json',
+);
-test('returns config from package directory when local binary is used and `.np-config.js` exists in package directory', async t => {
- const configs = await getConfigsWhenLocalBinaryIsUsed(path.resolve(fixtureBasePath, 'local2'));
- configs.forEach(config => t.deepEqual(config, {source: 'packagedir/.np-config.js'}));
-});
+test('returns config from home directory when global binary is used and `.np-config.js` as CJS exists in home directory',
+ useGlobalBinary, 'homedir2', 'homedir/.np-config.js',
+);
-test('returns config from package directory when local binary is used and `.np-config.cjs` exists in package directory', async t => {
- const configs = await getConfigsWhenLocalBinaryIsUsed(path.resolve(fixtureBasePath, 'local3'));
- configs.forEach(config => t.deepEqual(config, {source: 'packagedir/.np-config.cjs'}));
-});
+test('returns config from home directory when global binary is used and `.np-config.cjs` exists in home directory',
+ useGlobalBinary, 'homedir3', 'homedir/.np-config.cjs',
+);
+
+test.failing('returns config from home directory when global binary is used and `.np-config.js` as ESM exists in home directory',
+ useGlobalBinary, 'homedir4', 'homedir/.np-config.js',
+);
+
+test('returns config from home directory when global binary is used and `.np-config.mjs` exists in home directory',
+ useGlobalBinary, 'homedir5', 'homedir/.np-config.mjs',
+);
+
+test('returns config from package directory when local binary is used and `package.json` exists in package directory',
+ useLocalBinary, 'pkg-dir', 'package.json',
+);
+
+test('returns config from package directory when local binary is used and `.np-config.json` exists in package directory',
+ useLocalBinary, 'local1', 'packagedir/.np-config.json',
+);
+
+test('returns config from package directory when local binary is used and `.np-config.js` as CJS exists in package directory',
+ useLocalBinary, 'local2', 'packagedir/.np-config.js',
+);
+
+test('returns config from package directory when local binary is used and `.np-config.cjs` exists in package directory',
+ useLocalBinary, 'local3', 'packagedir/.np-config.cjs',
+);
+
+test('returns config from package directory when local binary is used and `.np-config.js` as ESM exists in package directory',
+ useLocalBinary, 'local4', 'packagedir/.np-config.js',
+);
+
+test('returns config from package directory when local binary is used and `.np-config.mjs` exists in package directory',
+ useLocalBinary, 'local5', 'packagedir/.np-config.mjs',
+);
diff --git a/test/fixtures/config/homedir4/.np-config.js b/test/fixtures/config/homedir4/.np-config.js
new file mode 100644
index 00000000..a91f20d0
--- /dev/null
+++ b/test/fixtures/config/homedir4/.np-config.js
@@ -0,0 +1,3 @@
+export default {
+ source: 'homedir/.np-config.js'
+};
diff --git a/test/fixtures/config/homedir5/.np-config.mjs b/test/fixtures/config/homedir5/.np-config.mjs
new file mode 100644
index 00000000..7565b8fb
--- /dev/null
+++ b/test/fixtures/config/homedir5/.np-config.mjs
@@ -0,0 +1,3 @@
+export default {
+ source: 'homedir/.np-config.mjs'
+};
diff --git a/test/fixtures/config/local4/.np-config.js b/test/fixtures/config/local4/.np-config.js
new file mode 100644
index 00000000..41bc0e49
--- /dev/null
+++ b/test/fixtures/config/local4/.np-config.js
@@ -0,0 +1,3 @@
+export default {
+ source: 'packagedir/.np-config.js'
+};
diff --git a/test/fixtures/config/local4/package.json b/test/fixtures/config/local4/package.json
new file mode 100644
index 00000000..6509d65a
--- /dev/null
+++ b/test/fixtures/config/local4/package.json
@@ -0,0 +1,4 @@
+{
+ "name": "use-type-module-for-config-fixtures",
+ "type": "module"
+}
diff --git a/test/fixtures/config/local5/.np-config.mjs b/test/fixtures/config/local5/.np-config.mjs
new file mode 100644
index 00000000..90b0f8f5
--- /dev/null
+++ b/test/fixtures/config/local5/.np-config.mjs
@@ -0,0 +1,3 @@
+export default {
+ source: 'packagedir/.np-config.mjs'
+};
diff --git a/test/fixtures/config/package.json b/test/fixtures/config/package.json
new file mode 100644
index 00000000..7ad6eeb0
--- /dev/null
+++ b/test/fixtures/config/package.json
@@ -0,0 +1,3 @@
+{
+ "name": "override-type-module-for-config-fixtures"
+}
diff --git a/test/fixtures/listr-renderer.js b/test/fixtures/listr-renderer.js
index ee5982e6..9a9f2581 100644
--- a/test/fixtures/listr-renderer.js
+++ b/test/fixtures/listr-renderer.js
@@ -1,6 +1,6 @@
let tasks;
-class SilentRenderer {
+export class SilentRenderer {
constructor(_tasks) {
tasks = _tasks;
}
@@ -13,9 +13,11 @@ class SilentRenderer {
return true;
}
- render() { }
+ static clearTasks() {
+ tasks = [];
+ }
- end() { }
-}
+ render() {}
-module.exports.SilentRenderer = SilentRenderer;
+ end() {}
+}
diff --git a/test/git-tasks.js b/test/git-tasks.js
index 56f5422a..d390d0c5 100644
--- a/test/git-tasks.js
+++ b/test/git-tasks.js
@@ -1,133 +1,144 @@
import test from 'ava';
-import execaStub from 'execa_test_double';
-import mockery from 'mockery';
-import {SilentRenderer} from './fixtures/listr-renderer';
-
-let testedModule;
-
-const run = async listr => {
- listr.setRenderer(SilentRenderer);
- await listr.run();
-};
-
-test.before(() => {
- mockery.registerMock('execa', execaStub.execa);
- mockery.enable({
- useCleanCache: true,
- warnOnReplace: false,
- warnOnUnregistered: false
- });
- testedModule = require('../source/git-tasks');
-});
+import {SilentRenderer} from './fixtures/listr-renderer.js';
+import {
+ _stubExeca,
+ run,
+ assertTaskFailed,
+ assertTaskDoesntExist,
+} from './_utils.js';
+
+/** @type {(...args: ReturnType<_stubExeca>) => Promise} */
+const stubExeca = _stubExeca('../source/git-tasks.js');
-test.beforeEach(() => {
- execaStub.resetStub();
+test.afterEach(() => {
+ SilentRenderer.clearTasks();
});
test.serial('should fail when release branch is not specified, current branch is not the release branch, and publishing from any branch not permitted', async t => {
- execaStub.createStub([
- {
- command: 'git symbolic-ref --short HEAD',
- exitCode: 0,
- stdout: 'feature'
- }
- ]);
- await t.throwsAsync(run(testedModule({branch: 'master'})),
- {message: 'Not on `master` branch. Use --any-branch to publish anyway, or set a different release branch using --branch.'});
- t.true(SilentRenderer.tasks.some(task => task.title === 'Check current branch' && task.hasFailed()));
+ const gitTasks = await stubExeca(t, [{
+ command: 'git symbolic-ref --short HEAD',
+ exitCode: 0,
+ stdout: 'feature',
+ }]);
+
+ await t.throwsAsync(
+ run(gitTasks({branch: 'master'})),
+ {message: 'Not on `master` branch. Use --any-branch to publish anyway, or set a different release branch using --branch.'},
+ );
+
+ assertTaskFailed(t, 'Check current branch');
});
test.serial('should fail when current branch is not the specified release branch and publishing from any branch not permitted', async t => {
- execaStub.createStub([
- {
- command: 'git symbolic-ref --short HEAD',
- exitCode: 0,
- stdout: 'feature'
- }
- ]);
- await t.throwsAsync(run(testedModule({branch: 'release'})),
- {message: 'Not on `release` branch. Use --any-branch to publish anyway, or set a different release branch using --branch.'});
- t.true(SilentRenderer.tasks.some(task => task.title === 'Check current branch' && task.hasFailed()));
+ const gitTasks = await stubExeca(t, [{
+ command: 'git symbolic-ref --short HEAD',
+ exitCode: 0,
+ stdout: 'feature',
+ }]);
+
+ await t.throwsAsync(
+ run(gitTasks({branch: 'release'})),
+ {message: 'Not on `release` branch. Use --any-branch to publish anyway, or set a different release branch using --branch.'},
+ );
+
+ assertTaskFailed(t, 'Check current branch');
});
test.serial('should not fail when current branch not master and publishing from any branch permitted', async t => {
- execaStub.createStub([
+ const gitTasks = await stubExeca(t, [
{
command: 'git symbolic-ref --short HEAD',
exitCode: 0,
- stdout: 'feature'
+ stdout: 'feature',
},
{
command: 'git status --porcelain',
exitCode: 0,
- stdout: ''
+ stdout: '',
},
{
command: 'git rev-list --count --left-only @{u}...HEAD',
exitCode: 0,
- stdout: ''
- }
+ stdout: '',
+ },
]);
- await run(testedModule({anyBranch: true}));
- t.false(SilentRenderer.tasks.some(task => task.title === 'Check current branch'));
+
+ await t.notThrowsAsync(
+ run(gitTasks({anyBranch: true})),
+ );
+
+ assertTaskDoesntExist(t, 'Check current branch');
});
test.serial('should fail when local working tree modified', async t => {
- execaStub.createStub([
+ const gitTasks = await stubExeca(t, [
{
command: 'git symbolic-ref --short HEAD',
exitCode: 0,
- stdout: 'master'
+ stdout: 'master',
},
{
command: 'git status --porcelain',
exitCode: 0,
- stdout: 'M source/git-tasks.js'
- }
+ stdout: 'M source/git-tasks.js',
+ },
]);
- await t.throwsAsync(run(testedModule({branch: 'master'})), {message: 'Unclean working tree. Commit or stash changes first.'});
- t.true(SilentRenderer.tasks.some(task => task.title === 'Check local working tree' && task.hasFailed()));
+
+ await t.throwsAsync(
+ run(gitTasks({branch: 'master'})),
+ {message: 'Unclean working tree. Commit or stash changes first.'},
+ );
+
+ assertTaskFailed(t, 'Check local working tree');
});
test.serial('should fail when remote history differs', async t => {
- execaStub.createStub([
+ const gitTasks = await stubExeca(t, [
{
command: 'git symbolic-ref --short HEAD',
exitCode: 0,
- stdout: 'master'
+ stdout: 'master',
},
{
command: 'git status --porcelain',
exitCode: 0,
- stdout: ''
+ stdout: '',
},
{
command: 'git rev-list --count --left-only @{u}...HEAD',
exitCode: 0,
- stdout: '1'
- }
+ stdout: '1',
+ },
]);
- await t.throwsAsync(run(testedModule({branch: 'master'})), {message: 'Remote history differs. Please pull changes.'});
- t.true(SilentRenderer.tasks.some(task => task.title === 'Check remote history' && task.hasFailed()));
+
+ await t.throwsAsync(
+ run(gitTasks({branch: 'master'})),
+ {message: 'Remote history differs. Please pull changes.'},
+ );
+
+ assertTaskFailed(t, 'Check remote history');
});
test.serial('checks should pass when publishing from master, working tree is clean and remote history not different', async t => {
- execaStub.createStub([
+ const gitTasks = await stubExeca(t, [
{
command: 'git symbolic-ref --short HEAD',
exitCode: 0,
- stdout: 'master'
+ stdout: 'master',
},
{
command: 'git status --porcelain',
exitCode: 0,
- stdout: ''
+ stdout: '',
},
{
command: 'git rev-list --count --left-only @{u}...HEAD',
exitCode: 0,
- stdout: ''
- }
+ stdout: '',
+ },
]);
- await t.notThrowsAsync(run(testedModule({branch: 'master'})));
+
+ await t.notThrowsAsync(
+ run(gitTasks({branch: 'master'})),
+ );
});
diff --git a/test/hyperlinks.js b/test/hyperlinks.js
index 963a9663..c5879174 100644
--- a/test/hyperlinks.js
+++ b/test/hyperlinks.js
@@ -1,7 +1,7 @@
import test from 'ava';
import sinon from 'sinon';
import terminalLink from 'terminal-link';
-import {linkifyIssues, linkifyCommit, linkifyCommitRange} from '../source/util';
+import {linkifyIssues, linkifyCommit, linkifyCommitRange} from '../source/util.js';
const MOCK_REPO_URL = 'https://github.com/unicorn/rainbow';
const MOCK_COMMIT_HASH = '5063f8a';
diff --git a/test/index.js b/test/index.js
index d011b7be..dbf61745 100644
--- a/test/index.js
+++ b/test/index.js
@@ -1,7 +1,7 @@
import test from 'ava';
import sinon from 'sinon';
-import proxyquire from 'proxyquire';
-import np from '../source';
+import esmock from 'esmock';
+import np from '../source/index.js';
const defaultOptions = {
cleanup: true,
@@ -10,53 +10,55 @@ const defaultOptions = {
runPublish: true,
availability: {
isAvailable: false,
- isUnknown: false
- }
+ isUnknown: false,
+ },
+ renderer: 'silent',
};
-test('version is invalid', async t => {
- const message = 'Version should be either patch, minor, major, prepatch, preminor, premajor, prerelease, or a valid semver version.';
- await t.throwsAsync(np('foo', defaultOptions), message);
- await t.throwsAsync(np('4.x.3', defaultOptions), message);
+const npFails = test.macro(async (t, inputs, message) => {
+ await t.throwsAsync(
+ Promise.all(inputs.map(input => np(input, defaultOptions))),
+ {message},
+ );
});
-test('version is pre-release', async t => {
- const message = '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';
- await t.throwsAsync(np('premajor', defaultOptions), message);
- await t.throwsAsync(np('preminor', defaultOptions), message);
- await t.throwsAsync(np('prepatch', defaultOptions), message);
- await t.throwsAsync(np('prerelease', defaultOptions), message);
- await t.throwsAsync(np('10.0.0-0', defaultOptions), message);
- await t.throwsAsync(np('10.0.0-beta', defaultOptions), message);
-});
+test('version is invalid', npFails,
+ ['foo', '4.x.3'],
+ 'Version should be either patch, minor, major, prepatch, preminor, premajor, prerelease, or a valid semver version.',
+);
-test('errors on too low version', async t => {
- await t.throwsAsync(np('1.0.0', defaultOptions), /New version `1\.0\.0` should be higher than current version `\d+\.\d+\.\d+`/);
- await t.throwsAsync(np('1.0.0-beta', defaultOptions), /New version `1\.0\.0-beta` should be higher than current version `\d+\.\d+\.\d+`/);
-});
+test('version is pre-release', npFails,
+ ['premajor', 'preminor', 'prepatch', 'prerelease', '10.0.0-0', '10.0.0-beta'],
+ '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',
+);
+
+test('errors on too low version', npFails,
+ ['1.0.0', '1.0.0-beta'],
+ /New version `1\.0\.0(?:-beta)?` should be higher than current version `\d+\.\d+\.\d+`/,
+);
test('skip enabling 2FA if the package exists', async t => {
const enable2faStub = sinon.stub();
- const np = proxyquire('../source', {
- del: sinon.stub(),
- execa: sinon.stub().returns({pipe: sinon.stub()}),
- './prerequisite-tasks': sinon.stub(),
- './git-tasks': sinon.stub(),
- './git-util': {
+ const npMock = await esmock('../source/index.js', {
+ del: {deleteAsync: sinon.stub()},
+ execa: {execa: sinon.stub().returns({pipe: sinon.stub()})},
+ '../source/prerequisite-tasks.js': sinon.stub(),
+ '../source/git-tasks.js': sinon.stub(),
+ '../source/git-util.js': {
hasUpstream: sinon.stub().returns(true),
- pushGraceful: sinon.stub()
+ pushGraceful: sinon.stub(),
},
- './npm/enable-2fa': enable2faStub,
- './npm/publish': sinon.stub().returns({pipe: sinon.stub()})
- });
+ '../source/npm/enable-2fa.js': enable2faStub,
+ '../source/npm/publish.js': sinon.stub().returns({pipe: sinon.stub()}),
+ }, {});
- await t.notThrowsAsync(np('1.0.0', {
+ await t.notThrowsAsync(npMock('1.0.0', {
...defaultOptions,
availability: {
isAvailable: false,
- isUnknown: false
- }
+ isUnknown: false,
+ },
}));
t.true(enable2faStub.notCalled);
@@ -65,26 +67,26 @@ test('skip enabling 2FA if the package exists', async t => {
test('skip enabling 2FA if the `2fa` option is false', async t => {
const enable2faStub = sinon.stub();
- const np = proxyquire('../source', {
- del: sinon.stub(),
- execa: sinon.stub().returns({pipe: sinon.stub()}),
- './prerequisite-tasks': sinon.stub(),
- './git-tasks': sinon.stub(),
- './git-util': {
+ const npMock = await esmock('../source/index.js', {
+ del: {deleteAsync: sinon.stub()},
+ execa: {execa: sinon.stub().returns({pipe: sinon.stub()})},
+ '../source/prerequisite-tasks.js': sinon.stub(),
+ '../source/git-tasks.js': sinon.stub(),
+ '../source/git-util.js': {
hasUpstream: sinon.stub().returns(true),
- pushGraceful: sinon.stub()
+ pushGraceful: sinon.stub(),
},
- './npm/enable-2fa': enable2faStub,
- './npm/publish': sinon.stub().returns({pipe: sinon.stub()})
+ '../source/npm/enable-2fa.js': enable2faStub,
+ '../source/npm/publish.js': sinon.stub().returns({pipe: sinon.stub()}),
});
- await t.notThrowsAsync(np('1.0.0', {
+ await t.notThrowsAsync(npMock('1.0.0', {
...defaultOptions,
availability: {
isAvailable: true,
- isUnknown: false
+ isUnknown: false,
},
- '2fa': false
+ '2fa': false,
}));
t.true(enable2faStub.notCalled);
diff --git a/test/integration.js b/test/integration.js
index a26daab7..83d43d6e 100644
--- a/test/integration.js
+++ b/test/integration.js
@@ -1,11 +1,104 @@
-const test = require('ava');
-const execa = require('execa');
+/* eslint-disable ava/no-ignored-test-files */
+import process from 'node:process';
+import path from 'node:path';
+import fs from 'fs-extra';
+import test from 'ava';
+import {$} from 'execa';
+import {deleteAsync} from 'del';
+import * as gitUtil from '../source/git-util.js';
+import * as util from '../source/util.js';
+
+test.before(async t => {
+ await fs.emptyDir('integration');
+ process.chdir('integration');
+
+ await $`git init`;
+ await t.throwsAsync(gitUtil.latestTag(), undefined, 'prerequisites not met: repository should not contain any tags');
+
+ await fs.createFile('temp');
+ await $`git add .`;
+ await $`git commit -m 'init'`;
+ await deleteAsync('temp');
+});
test.after.always(async () => {
- await execa('git', ['submodule', 'update', '--remote']);
+ process.chdir('..');
+ await deleteAsync('integration');
+});
+
+test.afterEach.always(async t => {
+ if (typeof t.context.teardown === 'function') {
+ await t.context.teardown();
+ }
});
-test('Integration tests', async t => {
- await execa('npx', ['ava'], {cwd: 'integration-test'});
- t.pass();
+test.serial('files to package with tags added', async t => {
+ await $`git tag v0.0.0`;
+ await fs.createFile('new');
+ await fs.createFile('index.js');
+ await $`git add new index.js`;
+ await $`git commit -m "added"`;
+
+ t.context.teardown = async () => {
+ await $`git rm new`;
+ await $`git rm index.js`;
+ await $`git tag -d v0.0.0`;
+ await $`git commit -m "deleted"`;
+ };
+
+ t.deepEqual(
+ await util.getNewFiles({files: ['*.js']}),
+ {unpublished: ['new'], firstTime: ['index.js']},
+ );
+});
+
+test.serial.failing('file `new` to package without tags added', async t => {
+ await fs.createFile('new');
+ await fs.createFile('index.js');
+
+ t.context.teardown = async () => {
+ await deleteAsync(['new', 'index.js']);
+ };
+
+ t.deepEqual(
+ await util.getNewFiles({files: ['index.js']}),
+ {unpublished: ['new'], firstTime: ['index.js']},
+ );
+});
+
+test.serial('files with long pathnames added', async t => {
+ const longPath = path.join('veryLonggggggDirectoryName', 'veryLonggggggDirectoryName');
+ const filePath1 = path.join(longPath, 'file1');
+ const filePath2 = path.join(longPath, 'file2');
+
+ await $`git tag v0.0.0`;
+ await fs.mkdir(longPath, {recursive: true});
+ await fs.createFile(filePath1);
+ await fs.createFile(filePath2);
+ await $`git add ${filePath1} ${filePath2}`;
+ await $`git commit -m "added"`;
+
+ t.context.teardown = async () => {
+ await $`git rm -r ${longPath}`;
+ await $`git tag -d v0.0.0`;
+ await $`git commit -m "deleted"`;
+ };
+
+ t.deepEqual(
+ await util.getNewFiles({files: ['*.js']}),
+ {unpublished: [filePath1, filePath2], firstTime: []},
+ );
+});
+
+test.serial('no new files added', async t => {
+ await $`git tag v0.0.0`;
+
+ t.context.teardown = async () => {
+ await $`git tag -d v0.0.0`;
+ };
+
+ t.deepEqual(
+ await util.getNewFiles({files: ['*.js']}),
+ {unpublished: [], firstTime: []},
+ );
});
diff --git a/test/npmignore.js b/test/npmignore.js
index 8bb40c97..4f02492a 100644
--- a/test/npmignore.js
+++ b/test/npmignore.js
@@ -1,6 +1,6 @@
-import path from 'path';
+import path from 'node:path';
import test from 'ava';
-import proxyquire from 'proxyquire';
+import esmock from 'esmock';
const newFiles = [
'source/ignore.txt',
@@ -8,167 +8,111 @@ const newFiles = [
'.hg',
'test/file.txt',
'readme.md',
- 'README.txt'
+ 'README.txt',
];
-test('ignored files using file-attribute in package.json with one file', async t => {
- const testedModule = proxyquire('../source/npm/util', {
- 'pkg-dir':
- {
- sync: () => path.resolve('test', 'fixtures', 'package')
- }
- });
- t.deepEqual(await testedModule.getNewAndUnpublishedFiles({files: ['pay_attention.txt']}, newFiles), ['source/ignore.txt']);
-});
-
-test('ignored file using file-attribute in package.json with directory', async t => {
- const testedModule = proxyquire('../source/npm/util', {
- 'pkg-dir':
- {
- sync: () => path.resolve('test', 'fixtures', 'package')
- }
- });
- t.deepEqual(await testedModule.getNewAndUnpublishedFiles({files: ['source']}, newFiles), []);
-});
-
-test('ignored test files using files attribute and directory structure in package.json', async t => {
- const testedModule = proxyquire('../source/npm/util', {
- 'pkg-dir':
- {
- sync: () => path.resolve('test', 'fixtures', 'package')
- }
- });
- t.deepEqual(await testedModule.getNewAndUnpublishedFiles({files: ['source'], directories: {test: 'test-tap'}}, newFiles), ['test/file.txt']);
- t.deepEqual(await testedModule.getNewAndUnpublishedFiles({files: ['source'], directories: {test: ['test-tap']}}, newFiles), ['test/file.txt']);
-});
-
-test('ignored files using .npmignore', async t => {
- const testedModule = proxyquire('../source/npm/util', {
- 'pkg-dir':
- {
- sync: () => path.resolve('test', 'fixtures', 'npmignore')
- }
- });
- t.deepEqual(await testedModule.getNewAndUnpublishedFiles({name: 'npmignore'}, newFiles), ['source/ignore.txt']);
-});
-
-test('ignored test files using files attribute and .npmignore', async t => {
- const testedModule = proxyquire('../source/npm/util', {
- 'pkg-dir':
- {
- sync: () => path.resolve('test', 'fixtures', 'npmignore')
- }
- });
- t.deepEqual(await testedModule.getNewAndUnpublishedFiles({directories: {test: 'test-tap'}}, newFiles), ['source/ignore.txt', 'test/file.txt']);
- t.deepEqual(await testedModule.getNewAndUnpublishedFiles({directories: {test: ['test-tap']}}, newFiles), ['source/ignore.txt', 'test/file.txt']);
-});
-
-test('ignored files - dot files using files attribute', async t => {
- const testedModule = proxyquire('../source/npm/util', {
- 'pkg-dir':
- {
- sync: () => path.resolve('test', 'fixtures', 'package')
- }
- });
- t.deepEqual(await testedModule.getNewAndUnpublishedFiles({files: ['source']}, ['test/.dotfile']), []);
-});
-
-test('ignored files - dot files using .npmignore', async t => {
- const testedModule = proxyquire('../source/npm/util', {
- 'pkg-dir':
- {
- sync: () => path.resolve('test', 'fixtures', 'npmignore')
- }
- });
- t.deepEqual(await testedModule.getNewAndUnpublishedFiles({}, ['test/.dot']), []);
-});
-
-test('ignored files - ignore strategy is not used', async t => {
- const testedModule = proxyquire('../source/npm/util', {
- 'pkg-dir':
- {
- sync: () => path.resolve('test', 'fixtures')
- }
- });
- t.deepEqual(await testedModule.getNewAndUnpublishedFiles({name: 'no ignore strategy'}, newFiles), []);
-});
-
-test('first time published files using file-attribute in package.json with one file', async t => {
- const testedModule = proxyquire('../source/npm/util', {
- 'pkg-dir':
- {
- sync: () => path.resolve('test', 'fixtures', 'package')
- }
- });
- t.deepEqual(await testedModule.getFirstTimePublishedFiles({files: ['pay_attention.txt']}, newFiles), ['source/pay_attention.txt']);
-});
-
-test('first time published files using file-attribute in package.json with directory', async t => {
- const testedModule = proxyquire('../source/npm/util', {
- 'pkg-dir':
- {
- sync: () => path.resolve('test', 'fixtures', 'package')
- }
- });
- t.deepEqual(await testedModule.getFirstTimePublishedFiles({files: ['source']}, newFiles), ['source/ignore.txt', 'source/pay_attention.txt']);
-});
-
-test('first time published files using .npmignore', async t => {
- const testedModule = proxyquire('../source/npm/util', {
- 'pkg-dir':
- {
- sync: () => path.resolve('test', 'fixtures', 'npmignore')
- }
- });
- t.deepEqual(await testedModule.getFirstTimePublishedFiles({name: 'npmignore'}, newFiles), ['source/pay_attention.txt']);
-});
-
-test('first time published dot files using files attribute', async t => {
- const testedModule = proxyquire('../source/npm/util', {
- 'pkg-dir':
- {
- sync: () => path.resolve('test', 'fixtures', 'package')
- }
- });
- t.deepEqual(await testedModule.getFirstTimePublishedFiles({files: ['source']}, ['source/.dotfile']), ['source/.dotfile']);
-});
-
-test('first time published dot files using .npmignore', async t => {
- const testedModule = proxyquire('../source/npm/util', {
- 'pkg-dir':
- {
- sync: () => path.resolve('test', 'fixtures', 'npmignore')
- }
- });
- t.deepEqual(await testedModule.getFirstTimePublishedFiles({}, ['source/.dotfile']), ['source/.dotfile']);
-});
-
-test('first time published files - ignore strategy is not used', async t => {
- const testedModule = proxyquire('../source/npm/util', {
- 'pkg-dir':
- {
- sync: () => path.resolve('test', 'fixtures')
- }
- });
- t.deepEqual(await testedModule.getFirstTimePublishedFiles({name: 'no ignore strategy'}, newFiles), ['source/ignore.txt', 'source/pay_attention.txt', 'test/file.txt']);
-});
-
-test('first time published files - empty files property', async t => {
- const testedModule = proxyquire('../source/npm/util', {
- 'pkg-dir':
- {
- sync: () => path.resolve('test', 'fixtures', 'package')
- }
- });
- t.deepEqual(await testedModule.getFirstTimePublishedFiles({files: []}, newFiles), []);
-});
-
-test('first time published files - .npmignore excludes everything', async t => {
- const testedModule = proxyquire('../source/npm/util', {
- 'pkg-dir':
- {
- sync: () => path.resolve('test', 'fixtures', 'npmignore')
- }
- });
- t.deepEqual(await testedModule.getFirstTimePublishedFiles({name: 'excluded everything'}, ['source/ignore.txt']), []);
-});
+const mockPkgDir = test.macro(async (t, paths, impl) => {
+ const testedModule = await esmock('../source/npm/util.js', {
+ 'pkg-dir': {packageDirectorySync: () => path.resolve(...paths)},
+ });
+
+ await impl(t, testedModule);
+});
+
+test.serial('ignored files using file-attribute in package.json with one file', mockPkgDir, ['test', 'fixtures', 'package'],
+ async (t, {getNewAndUnpublishedFiles}) => {
+ t.deepEqual(await getNewAndUnpublishedFiles({files: ['pay_attention.txt']}, newFiles), ['source/ignore.txt']);
+ },
+);
+
+test.serial('ignored file using file-attribute in package.json with directory', mockPkgDir, ['test', 'fixtures', 'package'],
+ async (t, {getNewAndUnpublishedFiles}) => {
+ t.deepEqual(await getNewAndUnpublishedFiles({files: ['source']}, newFiles), []);
+ },
+);
+
+test.serial('ignored test files using files attribute and directory structure in package.json', mockPkgDir, ['test', 'fixtures', 'package'],
+ async (t, {getNewAndUnpublishedFiles}) => {
+ t.deepEqual(await getNewAndUnpublishedFiles({files: ['source'], directories: {test: 'test-tap'}}, newFiles), ['test/file.txt']);
+ t.deepEqual(await getNewAndUnpublishedFiles({files: ['source'], directories: {test: ['test-tap']}}, newFiles), ['test/file.txt']);
+ },
+);
+
+test.serial('ignored files using .npmignore', mockPkgDir, ['test', 'fixtures', 'npmignore'],
+ async (t, {getNewAndUnpublishedFiles}) => {
+ t.deepEqual(await getNewAndUnpublishedFiles({name: 'npmignore'}, newFiles), ['source/ignore.txt']);
+ },
+);
+
+test.serial('ignored test files using files attribute and .npmignore', mockPkgDir, ['test', 'fixtures', 'npmignore'],
+ async (t, {getNewAndUnpublishedFiles}) => {
+ t.deepEqual(await getNewAndUnpublishedFiles({directories: {test: 'test-tap'}}, newFiles), ['source/ignore.txt', 'test/file.txt']);
+ t.deepEqual(await getNewAndUnpublishedFiles({directories: {test: ['test-tap']}}, newFiles), ['source/ignore.txt', 'test/file.txt']);
+ },
+);
+
+test.serial('ignored files - dot files using files attribute', mockPkgDir, ['test', 'fixtures', 'package'],
+ async (t, {getNewAndUnpublishedFiles}) => {
+ t.deepEqual(await getNewAndUnpublishedFiles({files: ['source']}, ['test/.dotfile']), []);
+ },
+);
+
+test.serial('ignored files - dot files using .npmignore', mockPkgDir, ['test', 'fixtures', 'npmignore'],
+ async (t, {getNewAndUnpublishedFiles}) => {
+ t.deepEqual(await getNewAndUnpublishedFiles({}, ['test/.dot']), []);
+ },
+);
+
+test.serial('ignored files - ignore strategy is not used', mockPkgDir, ['test', 'fixtures'],
+ async (t, {getNewAndUnpublishedFiles}) => {
+ t.deepEqual(await getNewAndUnpublishedFiles({name: 'no ignore strategy'}, newFiles), []);
+ },
+);
+
+test.serial('first time published files using file-attribute in package.json with one file', mockPkgDir, ['test', 'fixtures', 'package'],
+ async (t, {getFirstTimePublishedFiles}) => {
+ t.deepEqual(await getFirstTimePublishedFiles({files: ['pay_attention.txt']}, newFiles), ['source/pay_attention.txt']);
+ },
+);
+
+test.serial('first time published files using file-attribute in package.json with directory', mockPkgDir, ['test', 'fixtures', 'package'],
+ async (t, {getFirstTimePublishedFiles}) => {
+ t.deepEqual(await getFirstTimePublishedFiles({files: ['source']}, newFiles), ['source/ignore.txt', 'source/pay_attention.txt']);
+ },
+);
+
+test.serial('first time published files using .npmignore', mockPkgDir, ['test', 'fixtures', 'npmignore'],
+ async (t, {getFirstTimePublishedFiles}) => {
+ t.deepEqual(await getFirstTimePublishedFiles({name: 'npmignore'}, newFiles), ['source/pay_attention.txt']);
+ },
+);
+
+test.serial('first time published dot files using files attribute', mockPkgDir, ['test', 'fixtures', 'package'],
+ async (t, {getFirstTimePublishedFiles}) => {
+ t.deepEqual(await getFirstTimePublishedFiles({files: ['source']}, ['source/.dotfile']), ['source/.dotfile']);
+ },
+);
+
+test.serial('first time published dot files using .npmignore', mockPkgDir, ['test', 'fixtures', 'npmignore'],
+ async (t, {getFirstTimePublishedFiles}) => {
+ t.deepEqual(await getFirstTimePublishedFiles({}, ['source/.dotfile']), ['source/.dotfile']);
+ },
+);
+
+test.serial('first time published files - ignore strategy is not used', mockPkgDir, ['test', 'fixtures'],
+ async (t, {getFirstTimePublishedFiles}) => {
+ t.deepEqual(await getFirstTimePublishedFiles({name: 'no ignore strategy'}, newFiles), ['source/ignore.txt', 'source/pay_attention.txt', 'test/file.txt']);
+ },
+);
+
+test.serial('first time published files - empty files property', mockPkgDir, ['test', 'fixtures', 'package'],
+ async (t, {getFirstTimePublishedFiles}) => {
+ t.deepEqual(await getFirstTimePublishedFiles({files: []}, newFiles), []);
+ },
+);
+
+test.serial('first time published files - .npmignore excludes everything', mockPkgDir, ['test', 'fixtures', 'npmignore'],
+ async (t, {getFirstTimePublishedFiles}) => {
+ t.deepEqual(await getFirstTimePublishedFiles({name: 'excluded everything'}, ['source/ignore.txt']), []);
+ },
+);
diff --git a/test/prefix.js b/test/prefix.js
index 6ede56a6..7eb5ef8c 100644
--- a/test/prefix.js
+++ b/test/prefix.js
@@ -1,6 +1,7 @@
import test from 'ava';
-import proxyquire from 'proxyquire';
-import {getTagVersionPrefix} from '../source/util';
+import esmock from 'esmock';
+import {stripIndent} from 'common-tags';
+import {getTagVersionPrefix} from '../source/util.js';
test('get tag prefix', async t => {
t.is(await getTagVersionPrefix({yarn: false}), 'v');
@@ -8,11 +9,17 @@ test('get tag prefix', async t => {
});
test('no options passed', async t => {
- await t.throwsAsync(getTagVersionPrefix(), {message: 'Expected `options` to be of type `object` but received type `undefined`'});
- await t.throwsAsync(getTagVersionPrefix({}), {message: 'Expected object `options` to have keys `["yarn"]`'});
+ await t.throwsAsync(getTagVersionPrefix(), {message: stripIndent`
+ Expected argument to be of type \`object\` but received type \`undefined\`
+ Expected object to have keys \`["yarn"]\`
+ `});
+ await t.throwsAsync(getTagVersionPrefix({}), {message: 'Expected object to have keys `["yarn"]`'});
});
test.serial('defaults to "v" when command fails', async t => {
- proxyquire('../source/util', {execa: Promise.reject});
- t.is(await getTagVersionPrefix({yarn: true}), 'v');
+ const testedModule = await esmock('../source/util.js', {
+ execa: {default: Promise.reject},
+ });
+
+ t.is(await testedModule.getTagVersionPrefix({yarn: true}), 'v');
});
diff --git a/test/preid.js b/test/preid.js
index 2bdf5295..b83bcd84 100644
--- a/test/preid.js
+++ b/test/preid.js
@@ -1,5 +1,6 @@
import test from 'ava';
-import {getPreReleasePrefix} from '../source/util';
+import {stripIndent} from 'common-tags';
+import {getPreReleasePrefix} from '../source/util.js';
test('get preId postfix', async t => {
t.is(await getPreReleasePrefix({yarn: false}), '');
@@ -7,6 +8,9 @@ test('get preId postfix', async t => {
});
test('no options passed', async t => {
- await t.throwsAsync(getPreReleasePrefix(), {message: 'Expected `options` to be of type `object` but received type `undefined`'});
- await t.throwsAsync(getPreReleasePrefix({}), {message: 'Expected object `options` to have keys `["yarn"]`'});
+ await t.throwsAsync(getPreReleasePrefix(), {message: stripIndent`
+ Expected argument to be of type \`object\` but received type \`undefined\`
+ Expected object to have keys \`["yarn"]\`
+ `});
+ await t.throwsAsync(getPreReleasePrefix({}), {message: 'Expected object to have keys `["yarn"]`'});
});
diff --git a/test/prerequisite-tasks.js b/test/prerequisite-tasks.js
index 7c42f8ee..e693e6f8 100644
--- a/test/prerequisite-tasks.js
+++ b/test/prerequisite-tasks.js
@@ -1,244 +1,295 @@
+import process from 'node:process';
import test from 'ava';
-import execaStub from 'execa_test_double';
-import mockery from 'mockery';
-import version from '../source/version';
-import {SilentRenderer} from './fixtures/listr-renderer';
+import {readPackageUp} from 'read-pkg-up';
+import Version from '../source/version.js';
+import actualPrerequisiteTasks from '../source/prerequisite-tasks.js';
+import {SilentRenderer} from './fixtures/listr-renderer.js';
+import {
+ _stubExeca,
+ run,
+ assertTaskFailed,
+ assertTaskDisabled,
+} from './_utils.js';
-let testedModule;
+/** @type {(...args: ReturnType<_stubExeca>) => Promise} */
+const stubExeca = _stubExeca('../source/prerequisite-tasks.js');
+const {packageJson: pkg} = await readPackageUp();
-const run = async listr => {
- listr.setRenderer(SilentRenderer);
- await listr.run();
-};
-
-test.before(() => {
- mockery.registerMock('execa', execaStub.execa);
- mockery.enable({
- useCleanCache: true,
- warnOnReplace: false,
- warnOnUnregistered: false
- });
- testedModule = require('../source/prerequisite-tasks');
-});
-
-test.beforeEach(() => {
- execaStub.resetStub();
+test.afterEach(() => {
+ SilentRenderer.clearTasks();
});
test.serial('public-package published on npm registry: should fail when npm registry not pingable', async t => {
- execaStub.createStub([{
+ const prerequisiteTasks = await stubExeca(t, [{
command: 'npm ping',
exitCode: 1,
exitCodeName: 'EPERM',
stdout: '',
- stderr: 'failed'
+ stderr: 'failed',
}]);
- await t.throwsAsync(run(testedModule('1.0.0', {name: 'test'}, {})),
- {message: 'Connection to npm registry failed'});
- t.true(SilentRenderer.tasks.some(task => task.title === 'Ping npm registry' && task.hasFailed()));
+
+ await t.throwsAsync(
+ run(prerequisiteTasks('1.0.0', {name: 'test'}, {})),
+ {message: 'Connection to npm registry failed'},
+ );
+
+ assertTaskFailed(t, 'Ping npm registry');
});
test.serial('private package: should disable task pinging npm registry', async t => {
- execaStub.createStub([
- {
- command: 'git rev-parse --quiet --verify refs/tags/v2.0.0',
- exitCode: 0,
- stdout: ''
- }
- ]);
- await run(testedModule('2.0.0', {name: 'test', version: '1.0.0', private: true}, {yarn: false}));
- t.true(SilentRenderer.tasks.some(task => task.title === 'Ping npm registry' && !task.isEnabled()));
+ const prerequisiteTasks = await stubExeca(t, [{
+ command: 'git rev-parse --quiet --verify refs/tags/v2.0.0',
+ exitCode: 0,
+ stdout: '',
+ }]);
+
+ await t.notThrowsAsync(
+ run(prerequisiteTasks('2.0.0', {name: 'test', version: '1.0.0', private: true}, {yarn: false})),
+ );
+
+ assertTaskDisabled(t, 'Ping npm registry');
});
test.serial('external registry: should disable task pinging npm registry', async t => {
- execaStub.createStub([
- {
- command: 'git rev-parse --quiet --verify refs/tags/v2.0.0',
- exitCode: 0,
- stdout: ''
- }
- ]);
- await run(testedModule('2.0.0', {name: 'test', version: '1.0.0', publishConfig: {registry: 'http://my.io'}},
- {yarn: false}));
- t.true(SilentRenderer.tasks.some(task => task.title === 'Ping npm registry' && !task.isEnabled()));
+ const prerequisiteTasks = await stubExeca(t, [{
+ command: 'git rev-parse --quiet --verify refs/tags/v2.0.0',
+ exitCode: 0,
+ stdout: '',
+ }]);
+
+ await t.notThrowsAsync(
+ run(prerequisiteTasks('2.0.0', {name: 'test', version: '1.0.0', publishConfig: {registry: 'http://my.io'}}, {yarn: false})),
+ );
+
+ assertTaskDisabled(t, 'Ping npm registry');
});
test.serial('should fail when npm version does not match range in `package.json`', async t => {
- execaStub.createStub([
+ const prerequisiteTasks = await stubExeca(t, [
{
command: 'npm --version',
exitCode: 0,
- stdout: '6.0.0'
+ stdout: '6.0.0',
},
{
command: 'git rev-parse --quiet --verify refs/tags/v2.0.0',
exitCode: 0,
- stdout: ''
- }
+ stdout: '',
+ },
]);
- const depRange = require('../package.json').engines.npm;
- await t.throwsAsync(run(testedModule('2.0.0', {name: 'test', version: '1.0.0'}, {yarn: false})),
- {message: `Please upgrade to npm${depRange}`});
- t.true(SilentRenderer.tasks.some(task => task.title === 'Check npm version' && task.hasFailed()));
+
+ const depRange = pkg.engines.npm;
+
+ await t.throwsAsync(
+ run(prerequisiteTasks('2.0.0', {name: 'test', version: '1.0.0'}, {yarn: false})),
+ {message: `Please upgrade to npm${depRange}`},
+ );
+
+ assertTaskFailed(t, 'Check npm version');
});
test.serial('should fail when yarn version does not match range in `package.json`', async t => {
- execaStub.createStub([
+ const prerequisiteTasks = await stubExeca(t, [
{
command: 'yarn --version',
exitCode: 0,
- stdout: '1.0.0'
+ stdout: '1.0.0',
},
{
command: 'git rev-parse --quiet --verify refs/tags/v2.0.0',
exitCode: 0,
- stdout: ''
- }
+ stdout: '',
+ },
]);
- const depRange = require('../package.json').engines.yarn;
- await t.throwsAsync(run(testedModule('2.0.0', {name: 'test', version: '1.0.0'}, {yarn: true})),
- {message: `Please upgrade to yarn${depRange}`});
- t.true(SilentRenderer.tasks.some(task => task.title === 'Check yarn version' && task.hasFailed()));
+
+ const depRange = pkg.engines.yarn;
+
+ await t.throwsAsync(
+ run(prerequisiteTasks('2.0.0', {name: 'test', version: '1.0.0'}, {yarn: true})),
+ {message: `Please upgrade to yarn${depRange}`},
+ );
+
+ assertTaskFailed(t, 'Check yarn version');
});
test.serial('should fail when user is not authenticated at npm registry', async t => {
- execaStub.createStub([
+ const prerequisiteTasks = await stubExeca(t, [
{
command: 'npm whoami',
exitCode: 0,
- stdout: 'sindresorhus'
+ stdout: 'sindresorhus',
},
{
command: 'npm access ls-collaborators test',
exitCode: 0,
- stdout: '{"sindresorhus": "read"}'
- }
+ stdout: '{"sindresorhus": "read"}',
+ },
]);
+
process.env.NODE_ENV = 'P';
- await t.throwsAsync(run(testedModule('2.0.0', {name: 'test', version: '1.0.0'}, {yarn: false})),
- {message: 'You do not have write permissions required to publish this package.'});
+
+ await t.throwsAsync(
+ run(prerequisiteTasks('2.0.0', {name: 'test', version: '1.0.0'}, {yarn: false})),
+ {message: 'You do not have write permissions required to publish this package.'},
+ );
+
process.env.NODE_ENV = 'test';
- t.true(SilentRenderer.tasks.some(task => task.title === 'Verify user is authenticated' && task.hasFailed()));
+
+ assertTaskFailed(t, 'Verify user is authenticated');
});
test.serial('should fail when user is not authenticated at external registry', async t => {
- execaStub.createStub([
+ const prerequisiteTasks = await stubExeca(t, [
{
command: 'npm whoami --registry http://my.io',
exitCode: 0,
- stdout: 'sindresorhus'
+ stdout: 'sindresorhus',
},
{
command: 'npm access ls-collaborators test --registry http://my.io',
exitCode: 0,
- stdout: '{"sindresorhus": "read"}'
- }
+ stdout: '{"sindresorhus": "read"}',
+ },
+ {
+ command: 'npm access list collaborators test --json --registry http://my.io',
+ exitCode: 0,
+ stdout: '{"sindresorhus": "read"}',
+ },
]);
+
process.env.NODE_ENV = 'P';
- await t.throwsAsync(run(testedModule('2.0.0', {name: 'test', version: '1.0.0', publishConfig: {registry: 'http://my.io'}}, {yarn: false})),
- {message: 'You do not have write permissions required to publish this package.'});
+
+ await t.throwsAsync(
+ run(prerequisiteTasks('2.0.0', {name: 'test', version: '1.0.0', publishConfig: {registry: 'http://my.io'}}, {yarn: false})),
+ {message: 'You do not have write permissions required to publish this package.'},
+ );
+
process.env.NODE_ENV = 'test';
- t.true(SilentRenderer.tasks.some(task => task.title === 'Verify user is authenticated' && task.hasFailed()));
+
+ assertTaskFailed(t, 'Verify user is authenticated');
});
test.serial('private package: should disable task `verify user is authenticated`', async t => {
- execaStub.createStub([
- {
- command: 'git rev-parse --quiet --verify refs/tags/v2.0.0',
- exitCode: 0,
- stdout: ''
- }
- ]);
+ const prerequisiteTasks = await stubExeca(t, [{
+ command: 'git rev-parse --quiet --verify refs/tags/v2.0.0',
+ exitCode: 0,
+ stdout: '',
+ }]);
+
process.env.NODE_ENV = 'P';
- await run(testedModule('2.0.0', {name: 'test', version: '1.0.0', private: true}, {yarn: false}));
+
+ await t.notThrowsAsync(
+ run(prerequisiteTasks('2.0.0', {name: 'test', version: '1.0.0', private: true}, {yarn: false})),
+ );
+
process.env.NODE_ENV = 'test';
- t.true(SilentRenderer.tasks.some(task => task.title === 'Verify user is authenticated' && !task.isEnabled()));
+
+ assertTaskDisabled(t, 'Verify user is authenticated');
});
test.serial('should fail when git version does not match range in `package.json`', async t => {
- execaStub.createStub([
- {
- command: 'git version',
- exitCode: 0,
- stdout: 'git version 1.0.0'
- }
- ]);
- const depRange = require('../package.json').engines.git;
- await t.throwsAsync(run(testedModule('2.0.0', {name: 'test', version: '1.0.0'}, {yarn: false})),
- {message: `Please upgrade to git${depRange}`});
- t.true(SilentRenderer.tasks.some(task => task.title === 'Check git version' && task.hasFailed()));
+ const prerequisiteTasks = await stubExeca(t, [{
+ command: 'git version',
+ exitCode: 0,
+ stdout: 'git version 1.0.0',
+ }]);
+
+ const depRange = pkg.engines.git;
+
+ await t.throwsAsync(
+ run(prerequisiteTasks('2.0.0', {name: 'test', version: '1.0.0'}, {yarn: false})),
+ {message: `Please upgrade to git${depRange}`},
+ );
+
+ assertTaskFailed(t, 'Check git version');
});
-test.serial('should fail when git remote does not exists', async t => {
- execaStub.createStub([
- {
- command: 'git ls-remote origin HEAD',
- exitCode: 1,
- exitCodeName: 'EPERM',
- stderr: 'not found'
- }
- ]);
- await t.throwsAsync(run(testedModule('2.0.0', {name: 'test', version: '1.0.0'}, {yarn: false})),
- {message: 'not found'});
- t.true(SilentRenderer.tasks.some(task => task.title === 'Check git remote' && task.hasFailed()));
+test.serial('should fail when git remote does not exist', async t => {
+ const prerequisiteTasks = await stubExeca(t, [{
+ command: 'git ls-remote origin HEAD',
+ exitCode: 1,
+ exitCodeName: 'EPERM',
+ stderr: 'not found',
+ }]);
+
+ await t.throwsAsync(
+ run(prerequisiteTasks('2.0.0', {name: 'test', version: '1.0.0'}, {yarn: false})),
+ {message: 'not found'},
+ );
+
+ assertTaskFailed(t, 'Check git remote');
});
test.serial('should fail when version is invalid', async t => {
- await t.throwsAsync(run(testedModule('DDD', {name: 'test', version: '1.0.0'}, {yarn: false})),
- {message: `Version should be either ${version.SEMVER_INCREMENTS.join(', ')}, or a valid semver version.`});
- t.true(SilentRenderer.tasks.some(task => task.title === 'Validate version' && task.hasFailed()));
+ await t.throwsAsync(
+ run(actualPrerequisiteTasks('DDD', {name: 'test', version: '1.0.0'}, {yarn: false})),
+ {message: `Version should be either ${Version.SEMVER_INCREMENTS.join(', ')}, or a valid semver version.`},
+ );
+
+ assertTaskFailed(t, 'Validate version');
});
test.serial('should fail when version is lower as latest version', async t => {
- await t.throwsAsync(run(testedModule('0.1.0', {name: 'test', version: '1.0.0'}, {yarn: false})),
- {message: 'New version `0.1.0` should be higher than current version `1.0.0`'});
- t.true(SilentRenderer.tasks.some(task => task.title === 'Validate version' && task.hasFailed()));
+ await t.throwsAsync(
+ run(actualPrerequisiteTasks('0.1.0', {name: 'test', version: '1.0.0'}, {yarn: false})),
+ {message: 'New version `0.1.0` should be higher than current version `1.0.0`'},
+ );
+
+ assertTaskFailed(t, 'Validate version');
});
test.serial('should fail when prerelease version of public package without dist tag given', async t => {
- await t.throwsAsync(run(testedModule('2.0.0-1', {name: 'test', version: '1.0.0'}, {yarn: false})),
- {message: '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'});
- t.true(SilentRenderer.tasks.some(task => task.title === 'Check for pre-release version' && task.hasFailed()));
+ await t.throwsAsync(
+ run(actualPrerequisiteTasks('2.0.0-1', {name: 'test', version: '1.0.0'}, {yarn: false})),
+ {message: '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'},
+ );
+
+ assertTaskFailed(t, 'Check for pre-release version');
});
test.serial('should not fail when prerelease version of public package with dist tag given', async t => {
- execaStub.createStub([
- {
- command: 'git rev-parse --quiet --verify refs/tags/v2.0.0',
- stdout: ''
- }
- ]);
- await t.notThrowsAsync(run(testedModule('2.0.0-1', {name: 'test', version: '1.0.0'}, {yarn: false, tag: 'pre'})));
+ const prerequisiteTasks = await stubExeca(t, [{
+ command: 'git rev-parse --quiet --verify refs/tags/v2.0.0',
+ stdout: '',
+ }]);
+
+ await t.notThrowsAsync(
+ run(prerequisiteTasks('2.0.0-1', {name: 'test', version: '1.0.0'}, {yarn: false, tag: 'pre'})),
+ );
});
test.serial('should not fail when prerelease version of private package without dist tag given', async t => {
- execaStub.createStub([
- {
- command: 'git rev-parse --quiet --verify refs/tags/v2.0.0',
- stdout: ''
- }
- ]);
- await t.notThrowsAsync(run(testedModule('2.0.0-1', {name: 'test', version: '1.0.0', private: true}, {yarn: false})));
+ const prerequisiteTasks = await stubExeca(t, [{
+ command: 'git rev-parse --quiet --verify refs/tags/v2.0.0',
+ stdout: '',
+ }]);
+
+ await t.notThrowsAsync(
+ run(prerequisiteTasks('2.0.0-1', {name: 'test', version: '1.0.0', private: true}, {yarn: false})),
+ );
});
test.serial('should fail when git tag already exists', async t => {
- execaStub.createStub([
- {
- command: 'git rev-parse --quiet --verify refs/tags/v2.0.0',
- stdout: 'vvb'
- }
- ]);
- await t.throwsAsync(run(testedModule('2.0.0', {name: 'test', version: '1.0.0'}, {yarn: false})),
- {message: 'Git tag `v2.0.0` already exists.'});
- t.true(SilentRenderer.tasks.some(task => task.title === 'Check git tag existence' && task.hasFailed()));
+ const prerequisiteTasks = await stubExeca(t, [{
+ command: 'git rev-parse --quiet --verify refs/tags/v2.0.0',
+ stdout: 'vvb',
+ }]);
+
+ await t.throwsAsync(
+ run(prerequisiteTasks('2.0.0', {name: 'test', version: '1.0.0'}, {yarn: false})),
+ {message: 'Git tag `v2.0.0` already exists.'},
+ );
+
+ assertTaskFailed(t, 'Check git tag existence');
});
test.serial('checks should pass', async t => {
- execaStub.createStub([
- {
- command: 'git rev-parse --quiet --verify refs/tags/v2.0.0',
- stdout: ''
- }
- ]);
- await t.notThrowsAsync(run(testedModule('2.0.0', {name: 'test', version: '1.0.0'}, {yarn: false})));
+ const prerequisiteTasks = await stubExeca(t, [{
+ command: 'git rev-parse --quiet --verify refs/tags/v2.0.0',
+ stdout: '',
+ }]);
+
+ await t.notThrowsAsync(
+ run(prerequisiteTasks('2.0.0', {name: 'test', version: '1.0.0'}, {yarn: false})),
+ );
});
diff --git a/test/version.js b/test/version.js
index 07b8c056..203fb4e2 100644
--- a/test/version.js
+++ b/test/version.js
@@ -1,139 +1,139 @@
import test from 'ava';
-import version from '../source/version';
+import Version from '../source/version.js';
test('version.SEMVER_INCREMENTS', t => {
- t.deepEqual(version.SEMVER_INCREMENTS, ['patch', 'minor', 'major', 'prepatch', 'preminor', 'premajor', 'prerelease']);
+ t.deepEqual(Version.SEMVER_INCREMENTS, ['patch', 'minor', 'major', 'prepatch', 'preminor', 'premajor', 'prerelease']);
});
test('version.PRERELEASE_VERSIONS', t => {
- t.deepEqual(version.PRERELEASE_VERSIONS, ['prepatch', 'preminor', 'premajor', 'prerelease']);
+ t.deepEqual(Version.PRERELEASE_VERSIONS, ['prepatch', 'preminor', 'premajor', 'prerelease']);
});
test('version.isValidInput', t => {
- t.false(version.isValidInput(null));
- t.false(version.isValidInput('foo'));
- t.false(version.isValidInput('1.0.0.0'));
-
- t.true(version.isValidInput('patch'));
- t.true(version.isValidInput('minor'));
- t.true(version.isValidInput('major'));
- t.true(version.isValidInput('prepatch'));
- t.true(version.isValidInput('preminor'));
- t.true(version.isValidInput('premajor'));
- t.true(version.isValidInput('prerelease'));
- t.true(version.isValidInput('1.0.0'));
- t.true(version.isValidInput('1.1.0'));
- t.true(version.isValidInput('1.0.1'));
- t.true(version.isValidInput('1.0.0-beta'));
- t.true(version.isValidInput('2.0.0-rc.2'));
+ t.false(Version.isValidInput(null));
+ t.false(Version.isValidInput('foo'));
+ t.false(Version.isValidInput('1.0.0.0'));
+
+ t.true(Version.isValidInput('patch'));
+ t.true(Version.isValidInput('minor'));
+ t.true(Version.isValidInput('major'));
+ t.true(Version.isValidInput('prepatch'));
+ t.true(Version.isValidInput('preminor'));
+ t.true(Version.isValidInput('premajor'));
+ t.true(Version.isValidInput('prerelease'));
+ t.true(Version.isValidInput('1.0.0'));
+ t.true(Version.isValidInput('1.1.0'));
+ t.true(Version.isValidInput('1.0.1'));
+ t.true(Version.isValidInput('1.0.0-beta'));
+ t.true(Version.isValidInput('2.0.0-rc.2'));
});
test('version.isPrerelease', t => {
- t.false(version('1.0.0').isPrerelease());
- t.false(version('1.1.0').isPrerelease());
- t.false(version('1.0.1').isPrerelease());
+ t.false(new Version('1.0.0').isPrerelease());
+ t.false(new Version('1.1.0').isPrerelease());
+ t.false(new Version('1.0.1').isPrerelease());
- t.true(version('1.0.0-beta').isPrerelease());
- t.true(version('2.0.0-rc.2').isPrerelease());
+ t.true(new Version('1.0.0-beta').isPrerelease());
+ t.true(new Version('2.0.0-rc.2').isPrerelease());
});
test('version.isPrereleaseOrIncrement', t => {
- t.false(version.isPrereleaseOrIncrement('patch'));
- t.false(version.isPrereleaseOrIncrement('minor'));
- t.false(version.isPrereleaseOrIncrement('major'));
-
- t.true(version.isPrereleaseOrIncrement('prepatch'));
- t.true(version.isPrereleaseOrIncrement('preminor'));
- t.true(version.isPrereleaseOrIncrement('premajor'));
- t.true(version.isPrereleaseOrIncrement('prerelease'));
+ t.false(Version.isPrereleaseOrIncrement('patch'));
+ t.false(Version.isPrereleaseOrIncrement('minor'));
+ t.false(Version.isPrereleaseOrIncrement('major'));
+
+ t.true(Version.isPrereleaseOrIncrement('prepatch'));
+ t.true(Version.isPrereleaseOrIncrement('preminor'));
+ t.true(Version.isPrereleaseOrIncrement('premajor'));
+ t.true(Version.isPrereleaseOrIncrement('prerelease'));
});
test('version.getNewVersionFrom', t => {
const message = 'Version should be either patch, minor, major, prepatch, preminor, premajor, prerelease, or a valid semver version.';
- t.throws(() => version('1.0.0').getNewVersionFrom('patchxxx'), message);
- t.throws(() => version('1.0.0').getNewVersionFrom('1.0.0.0'), message);
+ t.throws(() => new Version('1.0.0').getNewVersionFrom('patchxxx'), {message});
+ t.throws(() => new Version('1.0.0').getNewVersionFrom('1.0.0.0'), {message});
- t.is(version('1.0.0').getNewVersionFrom('patch'), '1.0.1');
- t.is(version('1.0.0').getNewVersionFrom('minor'), '1.1.0');
- t.is(version('1.0.0').getNewVersionFrom('major'), '2.0.0');
+ t.is(new Version('1.0.0').getNewVersionFrom('patch'), '1.0.1');
+ t.is(new Version('1.0.0').getNewVersionFrom('minor'), '1.1.0');
+ t.is(new Version('1.0.0').getNewVersionFrom('major'), '2.0.0');
- t.is(version('1.0.0-beta').getNewVersionFrom('major'), '1.0.0');
- t.is(version('1.0.0').getNewVersionFrom('prepatch'), '1.0.1-0');
- t.is(version('1.0.1-0').getNewVersionFrom('prepatch'), '1.0.2-0');
+ t.is(new Version('1.0.0-beta').getNewVersionFrom('major'), '1.0.0');
+ t.is(new Version('1.0.0').getNewVersionFrom('prepatch'), '1.0.1-0');
+ t.is(new Version('1.0.1-0').getNewVersionFrom('prepatch'), '1.0.2-0');
- t.is(version('1.0.0-0').getNewVersionFrom('prerelease'), '1.0.0-1');
- t.is(version('1.0.1-0').getNewVersionFrom('prerelease'), '1.0.1-1');
+ t.is(new Version('1.0.0-0').getNewVersionFrom('prerelease'), '1.0.0-1');
+ t.is(new Version('1.0.1-0').getNewVersionFrom('prerelease'), '1.0.1-1');
});
test('version.validate', t => {
const message = 'Version should be a valid semver version.';
- t.throws(() => version.validate('patch'), message);
- t.throws(() => version.validate('patchxxx'), message);
- t.throws(() => version.validate('1.0.0.0'), message);
+ t.throws(() => Version.validate('patch'), {message});
+ t.throws(() => Version.validate('patchxxx'), {message});
+ t.throws(() => Version.validate('1.0.0.0'), {message});
- t.notThrows(() => version.validate('1.0.0'));
- t.notThrows(() => version.validate('1.0.0-beta'));
- t.notThrows(() => version.validate('1.0.0-0'));
+ t.notThrows(() => Version.validate('1.0.0'));
+ t.notThrows(() => Version.validate('1.0.0-beta'));
+ t.notThrows(() => Version.validate('1.0.0-0'));
});
test('version.isGreaterThanOrEqualTo', t => {
- t.false(version('1.0.0').isGreaterThanOrEqualTo('0.0.1'));
- t.false(version('1.0.0').isGreaterThanOrEqualTo('0.1.0'));
+ t.false(new Version('1.0.0').isGreaterThanOrEqualTo('0.0.1'));
+ t.false(new Version('1.0.0').isGreaterThanOrEqualTo('0.1.0'));
- t.false(version('1.0.0').isGreaterThanOrEqualTo('1.0.0-0'));
- t.false(version('1.0.0').isGreaterThanOrEqualTo('1.0.0-beta'));
+ t.false(new Version('1.0.0').isGreaterThanOrEqualTo('1.0.0-0'));
+ t.false(new Version('1.0.0').isGreaterThanOrEqualTo('1.0.0-beta'));
- t.true(version('1.0.0').isGreaterThanOrEqualTo('1.0.0'));
- t.true(version('1.0.0').isGreaterThanOrEqualTo('1.0.1'));
- t.true(version('1.0.0').isGreaterThanOrEqualTo('1.1.0'));
- t.true(version('1.0.0').isGreaterThanOrEqualTo('2.0.0'));
+ t.true(new Version('1.0.0').isGreaterThanOrEqualTo('1.0.0'));
+ t.true(new Version('1.0.0').isGreaterThanOrEqualTo('1.0.1'));
+ t.true(new Version('1.0.0').isGreaterThanOrEqualTo('1.1.0'));
+ t.true(new Version('1.0.0').isGreaterThanOrEqualTo('2.0.0'));
- t.true(version('1.0.0').isGreaterThanOrEqualTo('2.0.0-0'));
- t.true(version('1.0.0').isGreaterThanOrEqualTo('2.0.0-beta'));
+ t.true(new Version('1.0.0').isGreaterThanOrEqualTo('2.0.0-0'));
+ t.true(new Version('1.0.0').isGreaterThanOrEqualTo('2.0.0-beta'));
});
test('version.isLowerThanOrEqualTo', t => {
- t.true(version('1.0.0').isLowerThanOrEqualTo('0.0.1'));
- t.true(version('1.0.0').isLowerThanOrEqualTo('0.1.0'));
+ t.true(new Version('1.0.0').isLowerThanOrEqualTo('0.0.1'));
+ t.true(new Version('1.0.0').isLowerThanOrEqualTo('0.1.0'));
- t.true(version('1.0.0').isLowerThanOrEqualTo('1.0.0-0'));
- t.true(version('1.0.0').isLowerThanOrEqualTo('1.0.0-beta'));
- t.true(version('1.0.0').isLowerThanOrEqualTo('1.0.0'));
+ t.true(new Version('1.0.0').isLowerThanOrEqualTo('1.0.0-0'));
+ t.true(new Version('1.0.0').isLowerThanOrEqualTo('1.0.0-beta'));
+ t.true(new Version('1.0.0').isLowerThanOrEqualTo('1.0.0'));
- t.false(version('1.0.0').isLowerThanOrEqualTo('1.0.1'));
- t.false(version('1.0.0').isLowerThanOrEqualTo('1.1.0'));
- t.false(version('1.0.0').isLowerThanOrEqualTo('2.0.0'));
+ t.false(new Version('1.0.0').isLowerThanOrEqualTo('1.0.1'));
+ t.false(new Version('1.0.0').isLowerThanOrEqualTo('1.1.0'));
+ t.false(new Version('1.0.0').isLowerThanOrEqualTo('2.0.0'));
- t.false(version('1.0.0').isLowerThanOrEqualTo('2.0.0-0'));
- t.false(version('1.0.0').isLowerThanOrEqualTo('2.0.0-beta'));
+ t.false(new Version('1.0.0').isLowerThanOrEqualTo('2.0.0-0'));
+ t.false(new Version('1.0.0').isLowerThanOrEqualTo('2.0.0-beta'));
});
test('version.satisfies', t => {
- t.true(version('2.15.8').satisfies('>=2.15.8 <3.0.0 || >=3.10.1'));
- t.true(version('2.99.8').satisfies('>=2.15.8 <3.0.0 || >=3.10.1'));
- t.true(version('3.10.1').satisfies('>=2.15.8 <3.0.0 || >=3.10.1'));
- t.true(version('6.7.0-next.0').satisfies('<6.8.0'));
- t.false(version('3.0.0').satisfies('>=2.15.8 <3.0.0 || >=3.10.1'));
- t.false(version('3.10.0').satisfies('>=2.15.8 <3.0.0 || >=3.10.1'));
+ t.true(new Version('2.15.8').satisfies('>=2.15.8 <3.0.0 || >=3.10.1'));
+ t.true(new Version('2.99.8').satisfies('>=2.15.8 <3.0.0 || >=3.10.1'));
+ t.true(new Version('3.10.1').satisfies('>=2.15.8 <3.0.0 || >=3.10.1'));
+ t.true(new Version('6.7.0-next.0').satisfies('<6.8.0'));
+ t.false(new Version('3.0.0').satisfies('>=2.15.8 <3.0.0 || >=3.10.1'));
+ t.false(new Version('3.10.0').satisfies('>=2.15.8 <3.0.0 || >=3.10.1'));
});
test('version.getAndValidateNewVersionFrom', t => {
- t.is(version.getAndValidateNewVersionFrom('patch', '1.0.0'), '1.0.1');
+ t.is(Version.getAndValidateNewVersionFrom('patch', '1.0.0'), '1.0.1');
t.throws(
- () => version.getAndValidateNewVersionFrom('patch', '1'),
- 'Version should be a valid semver version.'
+ () => Version.getAndValidateNewVersionFrom('patch', '1'),
+ {message: 'Version should be a valid semver version.'},
);
t.throws(
- () => version.getAndValidateNewVersionFrom('lol', '1.0.0'),
- `Version should be either ${version.SEMVER_INCREMENTS.join(', ')}, or a valid semver version.`
+ () => Version.getAndValidateNewVersionFrom('lol', '1.0.0'),
+ {message: `Version should be either ${Version.SEMVER_INCREMENTS.join(', ')}, or a valid semver version.`},
);
t.throws(
- () => version.getAndValidateNewVersionFrom('1.0.0', '2.0.0'),
- 'New version `1.0.0` should be higher than current version `2.0.0`'
+ () => Version.getAndValidateNewVersionFrom('1.0.0', '2.0.0'),
+ {message: 'New version `1.0.0` should be higher than current version `2.0.0`'},
);
});