Repository: semantic-release/npm
Pull request #48 (2018-01-27 06:16:28)

 feat: update version in `package-lock.json`
See the reason for updating `package-lock.json` in this [comment](https://github.com/semantic-release/git/pull/23#issuecomment-360955388).

This PR also essentially revert #22 as updating the version via regexp turned out to not be such a great idea:
- Some edges cases are not covered, in particular if any configuration in `package.json` contains the keyword `"version"`
- Impossible to update the  `package-lock.json` as it contains `"version"` multiple time

The `package.json`, `package-lock.json` and `npm-shrinkwrap` are now formatted like `npm version` [does](https://github.com/npm/npm/blob/latest/lib/version.js#L200).
That might result in situation in which we change the user format, but I think it's acceptable as `npm version` or `npm install XXX` would change it the same way. 


Patch:
diff --git a/lib/update-package-version.js b/lib/update-package-version.js
index cc71361..b757da1 100644
--- a/lib/update-package-version.js
+++ b/lib/update-package-version.js
@@ -1,21 +1,24 @@
 const path = require('path');
-const {readFile, writeFile, pathExists} = require('fs-extra');
+const {readJson, writeJson, pathExists} = require('fs-extra');
 
 module.exports = async (version, basePath, logger) => {
   const packagePath = path.join(basePath, 'package.json');
   const shrinkwrapPath = path.join(basePath, 'npm-shrinkwrap.json');
-  const pkg = (await readFile(packagePath)).toString();
+  const packageLockPath = path.join(basePath, 'package-lock.json');
+  const pkg = await readJson(packagePath);
 
-  await writeFile(packagePath, replaceVersion(pkg, version));
+  await writeJson(packagePath, {...pkg, ...{version}}, {spaces: 2});
   logger.log('Wrote version %s to %s', version, packagePath);
 
   if (await pathExists(shrinkwrapPath)) {
-    const shrinkwrap = (await readFile(shrinkwrapPath)).toString();
-    await writeFile(shrinkwrapPath, replaceVersion(shrinkwrap, version));
+    const shrinkwrap = await readJson(shrinkwrapPath);
+    await writeJson(shrinkwrapPath, {...shrinkwrap, ...{version}}, {spaces: 2});
     logger.log('Wrote version %s to %s', version, shrinkwrapPath);
   }
-};
 
-function replaceVersion(json, version) {
-  return json.replace(/("version"\s*:\s*")\S+?(\s*")/, `$1${version}$2`);
-}
+  if (await pathExists(packageLockPath)) {
+    const packageLock = await readJson(packageLockPath);
+    await writeJson(packageLockPath, {...packageLock, ...{version}}, {spaces: 2});
+    logger.log('Wrote version %s to %s', version, packageLockPath);
+  }
+};


Test patch:
diff --git a/test/update-package-version.test.js b/test/update-package-version.test.js
index ac167b0..a1582e3 100644
--- a/test/update-package-version.test.js
+++ b/test/update-package-version.test.js
@@ -1,5 +1,5 @@
 import test from 'ava';
-import {outputFile, readFile} from 'fs-extra';
+import {outputJson, readJson} from 'fs-extra';
 import tempy from 'tempy';
 import execa from 'execa';
 import {stub} from 'sinon';
@@ -21,72 +21,63 @@ test.afterEach.always(() => {
 });
 
 test.serial('Updade package.json', async t => {
-  const pkg = `{
-  "name": "test",
-  "description": "pacakage description",
-  "version":
-       "0.0.0-dev"   ,
-  "arr": [
-    1,
-        2, 3
-      ]
-}
-`;
-
   // Create package.json in repository root
-  await outputFile('./package.json', pkg);
+  await outputJson('./package.json', {version: '0.0.0-dev'});
 
   await updatePackageVersion('1.0.0', '.', t.context.logger);
 
   // Verify package.json has been updated
-  t.is((await readFile('./package.json')).toString(), pkg.replace('0.0.0-dev', '1.0.0'));
+  t.is((await readJson('./package.json')).version, '1.0.0');
 
   // Verify the logger has been called with the version updated
   t.deepEqual(t.context.log.args[0], ['Wrote version %s to %s', '1.0.0', 'package.json']);
 });
 
 test.serial('Updade package.json and npm-shrinkwrap.json', async t => {
-  const pkg = `{
-    "name": "test"
-    ,    "description":
-"pacakage description",
-
-    "version":      "0.0.0-dev"
-  }`;
   // Create package.json in repository root
-  await outputFile('./package.json', pkg);
+  await outputJson('./package.json', {version: '0.0.0-dev'});
   // Create a npm-shrinkwrap.json file
   await execa('npm', ['shrinkwrap']);
-  const shrinkwrap = (await readFile('./npm-shrinkwrap.json')).toString();
 
   await updatePackageVersion('1.0.0', '.', t.context.logger);
 
   // Verify package.json and npm-shrinkwrap.json have been updated
-  t.is((await readFile('./package.json')).toString(), pkg.replace('0.0.0-dev', '1.0.0'));
-  t.is((await readFile('./npm-shrinkwrap.json')).toString(), shrinkwrap.replace('0.0.0-dev', '1.0.0'));
+  t.is((await readJson('./package.json')).version, '1.0.0');
+  t.is((await readJson('./npm-shrinkwrap.json')).version, '1.0.0');
   // Verify the logger has been called with the version updated
   t.deepEqual(t.context.log.args[0], ['Wrote version %s to %s', '1.0.0', 'package.json']);
   t.deepEqual(t.context.log.args[1], ['Wrote version %s to %s', '1.0.0', 'npm-shrinkwrap.json']);
 });
 
-test.serial('Updade package.json and npm-shrinkwrap.json in a sub-directory', async t => {
-  const pkg = `{
-    "name": "test","description":"pacakage description","version":"0.0.0-dev"
-  }`;
+test.serial('Updade package.json and package-lock.json', async t => {
+  // Create package.json in repository root
+  await outputJson('./package.json', {version: '0.0.0-dev'});
+  // Create a npm-shrinkwrap.json file
+  await execa('npm', ['install']);
+
+  await updatePackageVersion('1.0.0', '.', t.context.logger);
 
+  // Verify package.json and npm-shrinkwrap.json have been updated
+  t.is((await readJson('./package.json')).version, '1.0.0');
+  t.is((await readJson('./package-lock.json')).version, '1.0.0');
+  // Verify the logger has been called with the version updated
+  t.deepEqual(t.context.log.args[0], ['Wrote version %s to %s', '1.0.0', 'package.json']);
+  t.deepEqual(t.context.log.args[1], ['Wrote version %s to %s', '1.0.0', 'package-lock.json']);
+});
+
+test.serial('Updade package.json and npm-shrinkwrap.json in a sub-directory', async t => {
   // Create package.json in repository root
-  await outputFile('./dist/package.json', pkg);
+  await outputJson('./dist/package.json', {version: '0.0.0-dev'});
   process.chdir('dist');
   // Create a npm-shrinkwrap.json file
   await execa('npm', ['shrinkwrap']);
   process.chdir('..');
-  const shrinkwrap = (await readFile('./dist/npm-shrinkwrap.json')).toString();
 
   await updatePackageVersion('1.0.0', 'dist', t.context.logger);
 
   // Verify package.json and npm-shrinkwrap.json have been updated
-  t.is((await readFile('./dist/package.json')).toString(), pkg.replace('0.0.0-dev', '1.0.0'));
-  t.is((await readFile('./dist/npm-shrinkwrap.json')).toString(), shrinkwrap.replace('0.0.0-dev', '1.0.0'));
+  t.is((await readJson('./dist/package.json')).version, '1.0.0');
+  t.is((await readJson('./dist/npm-shrinkwrap.json')).version, '1.0.0');
   // Verify the logger has been called with the version updated
   t.deepEqual(t.context.log.args[0], ['Wrote version %s to %s', '1.0.0', 'dist/package.json']);
   t.deepEqual(t.context.log.args[1], ['Wrote version %s to %s', '1.0.0', 'dist/npm-shrinkwrap.json']);