All files / lib/resource-types keep-a-changelog.js

100% Statements 155/155
100% Branches 20/20
100% Functions 8/8
100% Lines 155/155

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 1561x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 3x 3x 3x 10x 10x 10x 10x 10x 3x 3x 3x 3x 3x 2x 3x 1x 1x 3x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 3x 3x 3x 8x 8x 8x 8x 8x 3x 3x 3x 3x 2x 3x 1x 1x 3x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 5x 5x 3x 3x 3x 3x 3x 1x 3x 1x 1x 1x 1x 3x 3x 3x 5x 5x 1x 5x 4x 4x 5x 5x 5x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 4x 4x 4x 4x 4x 4x 1x 1x 1x 4x 1x 1x 1x 1x 1x 1x 1x 1x  
"use strict";
/**
 * Changelog resource following {@link https://keepachangelog.com|Keep a
 * Changelog} format.
 *
 * @module resource-types/keep-a-changelog
 */
import { parser, Release } from "keep-a-changelog";
import fs from "fs";
import stringFormat from "string-format";
 
/**
 * Set release version on the latest release within the changelog, with date
 * set to current date.
 *
 * @param {String} version - version value to set
 * @param {Object} resource - resource configuration
 * @param {String} resource.path - path to the changelog file
 * @param {Object} opts - optional settings
 * @param {Boolean} [opts.dryRun] - when true, changelog file won't be
 *   modified
 * @param {String} opts.tagFormat - SCM tag format used in release links,
 *   e.g. v{version}
 * @param {Function} cb - standard cb(err, result) callback
 */
function setReleaseVersion(version, resource, opts, cb) {
  const changelog = parser(fs.readFileSync(resource.path, "UTF-8"));
  changelog.tagNameBuilder = function (release) {
    // Use tagFormat option to format the version to be used as SCM tag
    const formattedVersion = stringFormat(opts.tagFormat, {
      version: release.version,
    });
    return formattedVersion;
  };
  const latestRelease = changelog.releases[0];
  latestRelease.setVersion(version);
  latestRelease.setDate(new Date());
  if (!opts.dryRun) {
    fs.writeFile(resource.path, changelog.toString(), cb);
  } else {
    cb();
  }
}
 
/**
 * Add a new Unreleased section to the top of the changelog.
 *
 * The version value is not written to the changelog, Unreleased section
 * represents the next pre-release version instead.
 *
 * @param {String} version - post-release version value, unused
 * @param {Object} resource - resource configuration
 * @param {String} resource.path - path to the changelog file
 * @param {Object} opts - optional settings
 * @param {Boolean} [opts.dryRun] - when true, changelog file won't be
 *   modified
 * @param {String} opts.tagFormat - SCM tag format used in release links,
 *   e.g. v{version}
 * @param {Function} cb - standard cb(err, result) callback
 */
function setPostReleaseVersion(version, resource, opts, cb) {
  const changelog = parser(fs.readFileSync(resource.path, "UTF-8"));
  changelog.tagNameBuilder = function (release) {
    // Use tagFormat option to format the version to be used as SCM tag
    const formattedVersion = stringFormat(opts.tagFormat, {
      version: release.version,
    });
    return formattedVersion;
  };
  const postRelease = new Release();
  changelog.addRelease(postRelease);
  if (!opts.dryRun) {
    fs.writeFile(resource.path, changelog.toString(), cb);
  } else {
    cb();
  }
}
 
/**
 * Get version value from the changelog's latest release.
 *
 * It's common for the latest release of a changelog to be 'Unreleased', in
 * order to indicate that it's the one currently in progress. Hence the
 * version is resolved in this order:
 * 1. when latest release has a version number, use that version
 * 2. when latest release is 'Unreleased' (version is undefined after
 *    parsing), use the previous release's version
 * 3. otherwise default to '0.0.0'
 *
 * @param {Object} resource - resource configuration
 * @param {String} resource.path - path to the changelog file
 * @param {Function} cb - standard cb(err, result) callback, result is the
 *   version value
 */
function getVersion(resource, cb) {
  function parseCb(err, changelog) {
    let version;
    if (!err) {
      const latestRelease = changelog.releases[0];
      const latestReleaseMinusOne = changelog.releases[1];
      if (latestRelease && latestRelease.version) {
        version = latestRelease.version;
      } else if (latestReleaseMinusOne && latestReleaseMinusOne.version) {
        version = latestReleaseMinusOne.version;
      } else {
        version = "0.0.0";
      }
    }
    cb(err, version);
  }
  function readCb(err, result) {
    if (err) {
      cb(err);
    } else {
      _parseWithValidation(result, parseCb);
    }
  }
  fs.readFile(resource.path, "UTF-8", readCb);
}
 
/**
 * Parse changelog content using keep-a-changelog parser.
 * This function also serves as a validator, where an error will be thrown.
 *
 * Due to the way keep-a-changelog parser works, it throws error directly
 * instead of passing it to callback, hence we rethrow the error with a more
 * descriptive message.
 *
 * Even though the current implementation wouldn't pass an error to callback,
 * we still keep the cb parameter for consistency and future-proofing.
 *
 * @private
 * @param {String} content - changelog content
 * @param {Function} cb - standard cb(err, result) callback, result is the
 *   parsed changelog
 * @throws {Error} when changelog content can't be parsed
 */
function _parseWithValidation(content, cb) {
  try {
    const changelog = parser(content);
    console.dir(changelog);
    cb(null, changelog);
  } catch (err) {
    const message = `Failed to parse changelog. Please fix the changelog following the keep a changelog format https://keepachangelog.com . Error: ${err.message}`;
    throw new Error(message);
  }
}
 
const exports = {
  setReleaseVersion: setReleaseVersion,
  setPostReleaseVersion: setPostReleaseVersion,
  getVersion: getVersion,
};
 
export { exports as default };