UNPKG

@kwaeri/xfmr

Version:

The @kwaeri/xfmr component module of the @kwaeri application framework.

259 lines 13.8 kB
/******************************************************************************* * @module kwaeri/xfmr * @version 0.4.0 * @license * Copyright © 2014 - 2022 Richard Winters <kirvedx@gmail.com> and contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * SPDX-License-Identifier: Apache-2.0 ******************************************************************************/ 'use strict'; import rs from 'replacestream'; export default class VersionBump { /** * Represents possible operations that could be performed upon a semantic version string * * @arg { Object } */ operations = { patch: 0, minor: 1, major: 2, version: 3, prerelease: 4 }; /** * Represents a semantic version string that will replace another found within a file stream * * @arg { string } */ replacementSemver = ""; /** * Represents one of the possible operations that could be performed upon a semantic version string * * @arg { number } */ replacementOperation = 0; /** * This is a regular expression inteded to parse a JSDoc-based version tag and ac- * companying value from the header comment of any applicable file. * * [NOTES] * * * (?<required> )+ <-- This is the group we want to exist. We're making * it have to exist for a match to happen, by surrou- * nding the entire regex with this capture group. * * - The matching '( and )' makes a 'group'. * - The '?<xxx>' inside the opening parenthesis _na- * mes_ the group (xxx canbe whatever). * - The '+' at the end means the group must exist 1 * or more times. * * * A '?:' inside the opening parenthesis would sig- * nify NOT to capture anything, typically a nested * set of parenthesis would then be provided to si- * gnify _what_ within the non-capturing group, SH- * OULD be captured, as you'll see in a moment. * * * (?<version> ) <-- Names the version string group * * * (@version:[ ]*) <-- Matches literally "@version:" and possibly 0 or * more spaces * * * We do not use \s, as that includes newlines - * which we do no want to allow. Therefore, we * use [ ]. * * The capture (or parenthesis) around this block * provide us with a group which will contain the * "@version" and all the spaces that were found * prior to the major component of our semver stri- * ng, so that we can build a new, modified, semver * - without breaking formatting! * * * (?:(?<major>\d+).){1} <-- This captures from 1 or more numbers till a period * (but does not capture the period) * * * The group is named major, the major version com- * ponent) * * (?:(?<minor>\d+).){1} <-- This captures from 1 or more numbers till a period * again, again 1 time, yet not the period - again. * * * The group is named minor, the minor version com- * ponent) * * (?:(?<patch>\d+)){1}))+ <-- This captures from 1 or more numbers till a period * again, again 1 time, It stops at a white space or * anything other than a number. * * * It's named the patch group (patch version group) * * It also closes off the version and required gro- * ups. * * The "+" following is the "+" of the required gr- * oups capture, enforcing that everything we've * covered so far must have happened 1 time, or no * match will be made at all (one will not be repo- * rted back, even if part of the components are * matched). * * * (?:(?<prerelease>-[\+\!\~a-zA-Z0-9]+))? * * The above regex follows all which we've covered alrea- * dy, and states that there is a capture group, named * 'prerelease', which consists of a hyphen, followed by * any character from a list which has been provided. * * * The hyphen must immediately follow the required * group in order for this match to succeed. * * The '[]' denotes a list, which includes \+, \!, * \~, a-z, A-Z, 0-9. Any other character fails - * or ends if following an allowed character - the * match. * * The '?' means that the entire match can possibl- * y exist, but does not have to. * if it does it will be matched, otherwise we wil- * l still be returned with the required match. * * And this is the regex we will use to parse for the semver, within a JSDoc-style * header, in any file that is passed to this plugin. * * The options passed to this plug-in will tell us whether or not we will be repla- * cing the entire semver string, or bumping a particular component., * * We'll use this regexp in a string.replace function - so that we can actually pr- * operly replace the string if need be. * * @arg { RegExp } */ regex = /((@version:[ ]*)((?:(\d+).){1}(?:(\d+).){1}(?:(\d+)){1}))+(?:(-[\+\!\~a-zA-Z0-9]+))?/gm; /** * Holds the default preKey,key, and postKey components of our plug-in's regex. * * @arg { Object } */ regexPieces = { preKey: "((", postKey: "[ ]*)((?:(\\d+).){1}(?:(\\d+).){1}(?:(\\d+)){1}))+(?:(-[\\+\\!\\~a-zA-Z0-9]+))?" }; /** * Class constructor */ constructor() { } /** * Method which operates on a file stream * * @param { File|Stream|Buffer } file - A file stream, buffer, or string for which we will operate upon the contents of * @param { Object } options - An object who's properties are options to this method * * @return { Stream } The file stream, with all operations applied */ bumpVersion(file, options) { // If file is null just return with out doing anything: if (file && file.hasOwnProperty("isNull") && ((typeof file.isNull == "function" && file.isNull()) || file.isNull)) return file; // If no options are provided, or any acceptable options at least, return with an error: if (!options || (!options.version && !options.type)) return -3; // -3 indicates that options were wrong or missing // See if a custom key has been provided: if (options.key) this.regex = RegExp(this.regexPieces.preKey + options.key + this.regexPieces.postKey, 'gm'); // Otherwise let's figure out what we're doing here: if (typeof options.version === 'string') { // We're manually setting the version string: this.replacementOperation = this.operations.version; // Store the version string we're replacing with: this.replacementSemver = options.version; } else { // If options.type is set to a string, we're bumping a component of the semver: if (!options.version && options.type && typeof options.type === 'string') { switch (options.type) { case 'patch': this.replacementOperation = this.operations.patch; break; case 'minor': this.replacementOperation = this.operations.minor; break; case 'major': this.replacementOperation = this.operations.major; break; case 'prerelease': this.replacementOperation = this.operations.prerelease; break; } } // Otherwise version may also be set, but to a negative number? Testing? // If options.version is equal to -1, we're just testing our RegExp: if (options.version === -1) { // Don't forget that match returns an array, so return the first element! const matched = String(file.contents).match(this.regex); if (matched && matched !== null && matched[0]) file.contents = Buffer.from(String(matched[0])); return file; } } // If file is a stream, leverage replacestream: if (file && file.hasOwnProperty("isStream") && ((typeof file.isStream == "function" && file.isStream()) || file.isStream)) file.contents = file.contents.pipe(rs(this.regex, this.replaceVersion.bind(this))); // If file is a buffer, return a new buffer: if (file && file.hasOwnProperty("isBuffer") && ((typeof file.isBuffer == "function" && file.isBuffer()) || file.isBuffer)) file.contents = Buffer.from(String(file.contents.toString()).replace(this.regex, this.replaceVersion.bind(this))); if (file && typeof file == "string" && file !== "") file = file.replace(this.regex, this.replaceVersion.bind(this)); // Return the file return file; } /** * Method which modifies a JSDoc-based version tag and its accompanying value based upon values supplied to the calling parent. * * @param { string } fullMatch - The full match from the regex * @param { string } group1 - The first nested capture group, includes "@version:" through to the end of the third semver component * @param { string } group2 - The second nested capture group, includes "@version" through to the semver, but not including any semver components * @param { string } group3 - The third nested capture group, includes all semver components save prerelease (major, minor, patch) * @param { string } group4 - The first of 4 capture groups nested 3 levels in, captures the major semver component * @param { string } group5 - The second of 4 capture groups nested 3 levels in, captures the minor semver component * @param { string } group6 - The third of 4 capture groups nested 3 levels in, captures the patch semver component * @param { string } group7 - The fourth of 4 capture groups nested 3 levels in, captures the prerelease semver component (if exists) */ replaceVersion(fullMatch, group1, group2, group3, group4, group5, group6, group7) { // Define a variable with which we can build a return string. // // [NOTE] - We'll statically append group2 since its just @version plug the spaces leading // up to the semver. We need to keep formatting as is, and this is how its done. let result = group2; switch (this.replacementOperation) { case this.operations.patch: // Patch version component result += group4 + '.' + group5 + '.' + ((parseInt(group6)) + 1); break; case this.operations.minor: // Minor version component result += group4 + '.' + ((parseInt(group5)) + 1) + '.0'; break; case this.operations.major: // Major version component result += ((parseInt(group4)) + 1) + '.0.0'; break; case this.operations.version: // Whole semver string result += this.replacementSemver; // Simply tac the replacement semver (provided by user) to the result string. break; case this.operations.prerelease: // Prerelease version component result += group4 + '.' + group5 + '.' + group6 + ((group7) ? ((parseInt(group7)) + 1) : '-1'); // A prerelease component may not exist, let's account for that: break; } return result; } } //# sourceMappingURL=version-bump.mjs.map