Repository: semantic-release/npm
Pull request #3 (2017-11-24 22:53:11)

Make semantic-release language agnostic
See semantic-release/semantic-release#524

codecov[bot]: # [Codecov](https://codecov.io/gh/semantic-release/npm/pull/3?src=pr&el=h1) Report
> Merging [#3](https://codecov.io/gh/semantic-release/npm/pull/3?src=pr&el=desc) into [master](https://codecov.io/gh/semantic-release/npm/commit/a3601b5181bbf8382d6b23d0529f2cc9aab14212?src=pr&el=desc) will **not change** coverage.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/semantic-release/npm/pull/3/graphs/tree.svg?src=pr&width=650&token=lMTfwvbCvW&height=150)](https://codecov.io/gh/semantic-release/npm/pull/3?src=pr&el=tree)

```diff
@@          Coverage Diff          @@
##           master     #3   +/-   ##
=====================================
  Coverage     100%   100%           
=====================================
  Files           9      9           
  Lines         100    106    +6     
  Branches       13     14    +1     
=====================================
+ Hits          100    106    +6
```


| [Impacted Files](https://codecov.io/gh/semantic-release/npm/pull/3?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [lib/verify.js](https://codecov.io/gh/semantic-release/npm/pull/3?src=pr&el=tree#diff-bGliL3ZlcmlmeS5qcw==) | `100% <ø> (ø)` | :arrow_up: |
| [index.js](https://codecov.io/gh/semantic-release/npm/pull/3?src=pr&el=tree#diff-aW5kZXguanM=) | `100% <100%> (ø)` | :arrow_up: |
| [lib/get-pkg.js](https://codecov.io/gh/semantic-release/npm/pull/3?src=pr&el=tree#diff-bGliL2dldC1wa2cuanM=) | `100% <100%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/semantic-release/npm/pull/3?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/semantic-release/npm/pull/3?src=pr&el=footer). Last update [a3601b5...8f72eda](https://codecov.io/gh/semantic-release/npm/pull/3?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/index.js b/index.js
index a44964a..10d44f3 100644
--- a/index.js
+++ b/index.js
@@ -1,16 +1,19 @@
-const {callbackify} = require('util');
+const getPkg = require('./lib/get-pkg');
 const verifyNpm = require('./lib/verify');
 const publishNpm = require('./lib/publish');
 const getLastReleaseNpm = require('./lib/get-last-release');
 
 let verified;
 
-async function verifyConditions(pluginConfig, {pkg, logger}) {
+async function verifyConditions(pluginConfig, {logger}) {
+  const pkg = await getPkg();
   await verifyNpm(pkg, logger);
   verified = true;
 }
 
-async function getLastRelease(pluginConfig, {pkg, logger}) {
+async function getLastRelease(pluginConfig, {logger}) {
+  // Reload package.json in case a previous external step updated it
+  const pkg = await getPkg();
   if (!verified) {
     await verifyNpm(pkg, logger);
     verified = true;
@@ -18,7 +21,9 @@ async function getLastRelease(pluginConfig, {pkg, logger}) {
   return getLastReleaseNpm(pkg, logger);
 }
 
-async function publish(pluginConfig, {pkg, nextRelease: {version}, logger}) {
+async function publish(pluginConfig, {nextRelease: {version}, logger}) {
+  // Reload package.json in case a previous external step updated it
+  const pkg = await getPkg();
   if (!verified) {
     await verifyNpm(pkg, logger);
     verified = true;
@@ -26,8 +31,4 @@ async function publish(pluginConfig, {pkg, nextRelease: {version}, logger}) {
   await publishNpm(version, logger);
 }
 
-module.exports = {
-  verifyConditions: callbackify(verifyConditions),
-  getLastRelease: callbackify(getLastRelease),
-  publish: callbackify(publish),
-};
+module.exports = {verifyConditions, getLastRelease, publish};
diff --git a/lib/get-pkg.js b/lib/get-pkg.js
new file mode 100644
index 0000000..dfb4ec8
--- /dev/null
+++ b/lib/get-pkg.js
@@ -0,0 +1,16 @@
+const readPkgUp = require('read-pkg-up');
+const SemanticReleaseError = require('@semantic-release/error');
+
+module.exports = async () => {
+  const {pkg} = await readPkgUp();
+
+  if (!pkg) {
+    throw new SemanticReleaseError('A package.json file is required to release on npm.', 'ENOPKG');
+  }
+
+  if (!pkg.name) {
+    throw new SemanticReleaseError('No "name" found in package.json.', 'ENOPKGNAME');
+  }
+
+  return pkg;
+};
diff --git a/lib/verify-pkg.js b/lib/verify-pkg.js
deleted file mode 100644
index 96d182c..0000000
--- a/lib/verify-pkg.js
+++ /dev/null
@@ -1,7 +0,0 @@
-const SemanticReleaseError = require('@semantic-release/error');
-
-module.exports = ({name}) => {
-  if (!name) {
-    throw new SemanticReleaseError('No "name" found in package.json.', 'ENOPKGNAME');
-  }
-};
diff --git a/lib/verify.js b/lib/verify.js
index f89a940..9e3e812 100644
--- a/lib/verify.js
+++ b/lib/verify.js
@@ -1,10 +1,8 @@
 const execa = require('execa');
 const SemanticReleaseError = require('@semantic-release/error');
-const verifyPkg = require('./verify-pkg');
 const setNpmrcAuth = require('./set-npmrc-auth');
 
 module.exports = async (pkg, logger) => {
-  verifyPkg(pkg);
   await setNpmrcAuth(pkg, logger);
   try {
     await execa('npm', ['whoami']);
diff --git a/package.json b/package.json
index f49f7df..65e60f0 100644
--- a/package.json
+++ b/package.json
@@ -22,6 +22,7 @@
     "nerf-dart": "^1.0.0",
     "npm-conf": "^1.1.3",
     "npm-registry-client": "^8.5.0",
+    "read-pkg-up": "^3.0.0",
     "registry-auth-token": "^3.3.1"
   },
   "devDependencies": {
@@ -39,7 +40,7 @@
     "nock": "^9.1.0",
     "nyc": "^11.2.1",
     "prettier": "~1.8.2",
-    "semantic-release": "^9.1.1",
+    "semantic-release": "^10.0.0",
     "sinon": "^4.1.2",
     "tempy": "^0.2.1",
     "xo": "^0.18.2"


Test patch:
diff --git a/test/get-pkg.test.js b/test/get-pkg.test.js
new file mode 100644
index 0000000..9711fa9
--- /dev/null
+++ b/test/get-pkg.test.js
@@ -0,0 +1,49 @@
+import test from 'ava';
+import {writeJson, writeFile} from 'fs-extra';
+import tempy from 'tempy';
+import getPkg from '../lib/get-pkg';
+
+test.beforeEach(t => {
+  // Save the current working diretory
+  t.context.cwd = process.cwd();
+  // Change current working directory to a temp directory
+  process.chdir(tempy.directory());
+});
+
+test.afterEach.always(t => {
+  // Restore the current working directory
+  process.chdir(t.context.cwd);
+});
+
+test.serial('Verify name and return parsed package.json', async t => {
+  const pkg = {name: 'package', version: '0.0.0'};
+  await writeJson('./package.json', pkg);
+
+  const result = await getPkg();
+  t.is(pkg.name, result.name);
+  t.is(pkg.version, result.version);
+});
+
+test.serial('Throw error if missing package.json', async t => {
+  const error = await t.throws(getPkg());
+
+  t.is(error.name, 'SemanticReleaseError');
+  t.is(error.code, 'ENOPKG');
+});
+
+test.serial('Throw error if missing package name', async t => {
+  await writeJson('./package.json', {version: '0.0.0'});
+
+  const error = await t.throws(getPkg());
+
+  t.is(error.name, 'SemanticReleaseError');
+  t.is(error.code, 'ENOPKGNAME');
+});
+
+test.serial('Throw error if package.json is malformed', async t => {
+  await writeFile('./package.json', "{name: 'package',}");
+
+  const error = await t.throws(getPkg());
+
+  t.is(error.name, 'JSONError');
+});
diff --git a/test/integration.test.js b/test/integration.test.js
index 01cbb49..2ded23f 100644
--- a/test/integration.test.js
+++ b/test/integration.test.js
@@ -1,4 +1,3 @@
-import {promisify} from 'util';
 import {writeJson, readFile, appendFile} from 'fs-extra';
 import test from 'ava';
 import execa from 'execa';
@@ -67,9 +66,9 @@ test.after.always(async () => {
 
 test.serial('Throws error if NPM token is invalid', async t => {
   process.env.NPM_TOKEN = 'wrong_token';
-  const error = await t.throws(
-    promisify(t.context.m.verifyConditions)({}, {pkg: {name: 'invalid-token'}, logger: t.context.logger})
-  );
+  const pkg = {name: 'published', version: '1.0.0', publishConfig: {registry: npmRegistry.url}};
+  await writeJson('./package.json', pkg);
+  const error = await t.throws(t.context.m.verifyConditions({}, {logger: t.context.logger}));
 
   t.true(error instanceof SemanticReleaseError);
   t.is(error.code, 'EINVALIDNPMTOKEN');
@@ -82,7 +81,8 @@ test.serial('Throws error if NPM token is invalid', async t => {
 test.serial('Verify npm auth and package', async t => {
   Object.assign(process.env, npmRegistry.authEnv);
   const pkg = {name: 'valid-token', publishConfig: {registry: npmRegistry.url}};
-  await t.notThrows(promisify(t.context.m.verifyConditions)({}, {pkg, logger: t.context.logger}));
+  await writeJson('./package.json', pkg);
+  await t.notThrows(t.context.m.verifyConditions({}, {logger: t.context.logger}));
 
   const npmrc = (await readFile('.npmrc')).toString();
   t.regex(npmrc, /_auth =/);
@@ -92,7 +92,8 @@ test.serial('Verify npm auth and package', async t => {
 test.serial('Return nothing if no version if published', async t => {
   Object.assign(process.env, npmRegistry.authEnv);
   const pkg = {name: 'not-published', publishConfig: {registry: npmRegistry.url}};
-  const nextRelease = await promisify(t.context.m.getLastRelease)({}, {pkg, logger: t.context.logger});
+  await writeJson('./package.json', pkg);
+  const nextRelease = await t.context.m.getLastRelease({}, {logger: t.context.logger});
 
   t.deepEqual(nextRelease, {});
 });
@@ -111,7 +112,7 @@ test.serial('Return last version published', async t => {
 
   await execa('npm', ['publish']);
 
-  const nextRelease = await promisify(t.context.m.getLastRelease)({}, {pkg, logger: t.context.logger});
+  const nextRelease = await t.context.m.getLastRelease({}, {logger: t.context.logger});
   t.is(nextRelease.version, '1.0.0');
 });
 
@@ -134,7 +135,7 @@ test.serial('Return last version published on a dist-tag', async t => {
   // Publish version 1.1.0 on next
   await execa('npm', ['publish', '--tag=next']);
 
-  const nextRelease = await promisify(t.context.m.getLastRelease)({}, {pkg, logger: t.context.logger});
+  const nextRelease = await t.context.m.getLastRelease({}, {logger: t.context.logger});
   t.is(nextRelease.version, '1.1.0');
 });
 
@@ -153,7 +154,7 @@ test.serial('Return nothing for an unpublished package', async t => {
   await execa('npm', ['publish']);
   await execa('npm', ['unpublish', 'unpublished', '--force']);
 
-  const nextRelease = await promisify(t.context.m.getLastRelease)({}, {pkg, logger: t.context.logger});
+  const nextRelease = await t.context.m.getLastRelease({}, {logger: t.context.logger});
   t.deepEqual(nextRelease, {});
 });
 
@@ -162,7 +163,7 @@ test.serial('Publish a package', async t => {
   const pkg = {name: 'publish', version: '1.0.0', publishConfig: {registry: npmRegistry.url}};
   await writeJson('./package.json', pkg);
 
-  await promisify(t.context.m.publish)({}, {pkg, logger: t.context.logger, nextRelease: {version: '1.0.0'}});
+  await t.context.m.publish({}, {logger: t.context.logger, nextRelease: {version: '1.0.0'}});
 
   t.is((await execa('npm', ['view', 'publish', 'version'])).stdout, '1.0.0');
 });
@@ -172,13 +173,13 @@ test.serial('Verify token and set up auth only on the fist call', async t => {
   const pkg = {name: 'test-module', version: '0.0.0-dev', publishConfig: {registry: npmRegistry.url}};
   await writeJson('./package.json', pkg);
 
-  await t.notThrows(promisify(t.context.m.verifyConditions)({}, {pkg, logger: t.context.logger}));
+  await t.notThrows(t.context.m.verifyConditions({}, {logger: t.context.logger}));
 
-  let nextRelease = await promisify(t.context.m.getLastRelease)({}, {pkg, logger: t.context.logger});
+  let nextRelease = await t.context.m.getLastRelease({}, {logger: t.context.logger});
   t.deepEqual(nextRelease, {});
 
-  await promisify(t.context.m.publish)({}, {pkg, logger: t.context.logger, nextRelease: {version: '1.0.0'}});
+  await t.context.m.publish({}, {logger: t.context.logger, nextRelease: {version: '1.0.0'}});
 
-  nextRelease = await promisify(t.context.m.getLastRelease)({}, {pkg, logger: t.context.logger});
+  nextRelease = await t.context.m.getLastRelease({}, {logger: t.context.logger});
   t.is(nextRelease.version, '1.0.0');
 });
diff --git a/test/set-npmrc-auth.test.js b/test/set-npmrc-auth.test.js
index ab8fa23..1bbcf1e 100644
--- a/test/set-npmrc-auth.test.js
+++ b/test/set-npmrc-auth.test.js
@@ -27,7 +27,7 @@ test.beforeEach(t => {
 });
 
 test.afterEach.always(t => {
-  // Clear `rc` from the npm cache as it cache the relative path of .npmrc files, preventing to load a new file after changing current working directory
+  // Clear `rc` from the npm cache as it cache the relative path of .npmrc files, preventing to load a new file after changing current working directory. See https://github.com/dominictarr/rc/issues/101
   clearModule('rc');
   // Restore process.env
   process.env = Object.assign({}, t.context.env);
diff --git a/test/update-package-version.test.js b/test/update-package-version.test.js
index 0b635a2..87e5821 100644
--- a/test/update-package-version.test.js
+++ b/test/update-package-version.test.js
@@ -1,5 +1,5 @@
-import {writeJson, readJson} from 'fs-extra';
 import test from 'ava';
+import {writeJson, readJson} from 'fs-extra';
 import tempy from 'tempy';
 import execa from 'execa';
 import {stub} from 'sinon';
diff --git a/test/verify-pkg.test.js b/test/verify-pkg.test.js
deleted file mode 100644
index edd325b..0000000
--- a/test/verify-pkg.test.js
+++ /dev/null
@@ -1,14 +0,0 @@
-import test from 'ava';
-import SemanticReleaseError from '@semantic-release/error';
-import verify from '../lib/verify-pkg';
-
-test('Verify name and repository', t => {
-  t.notThrows(() => verify({name: 'package'}));
-});
-
-test('Return error for missing package name', t => {
-  const error = t.throws(() => verify({repository: {url: 'http://github.com/whats/up.git'}}));
-
-  t.true(error instanceof SemanticReleaseError);
-  t.is(error.code, 'ENOPKGNAME');
-});