UNPKG

changelog-tools

Version:

A set of tools for changelog parsing and generation

663 lines (577 loc) 25.5 kB
"use strict"; (() => { var __getOwnPropNames = Object.getOwnPropertyNames; var __commonJS = (cb, mod) => function __require() { return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; }; // lib/cjs/lib/types.js var require_types = __commonJS({ "lib/cjs/lib/types.js"(exports) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ProcessingContext = exports.OpenChangelogVersion = exports.Changelog = exports.ConsolidatedEntry = exports.ChangelogVersion = exports.ListItemChar = exports.NewlineSymbol = void 0; var NewlineSymbol; (function(NewlineSymbol2) { NewlineSymbol2["AUTO"] = "auto"; NewlineSymbol2["LF"] = "\n"; NewlineSymbol2["CRLF"] = "\r\n"; NewlineSymbol2["CR"] = "\r"; })(NewlineSymbol || (exports.NewlineSymbol = NewlineSymbol = {})); var ListItemChar; (function(ListItemChar2) { ListItemChar2["STAR"] = "*"; ListItemChar2["DASH"] = "-"; ListItemChar2["AUTO"] = "auto"; })(ListItemChar || (exports.ListItemChar = ListItemChar = {})); var ChangelogVersion = class { constructor() { this.body = ""; this.description = ""; this.parsed = { _: [] }; } get cleanedBody() { let result = ""; if (this.description) { result += `${this.description} `; } result += Object.entries(this.parsed).filter(([key]) => key !== "_").map(([type, texts]) => { return `### ${type} ` + texts.map((li) => `* ${li}`).join("\n"); }).join("\n\n"); return result; } toJSON() { return Object.assign(Object.assign({}, this), { cleanedBody: this.cleanedBody }); } }; exports.ChangelogVersion = ChangelogVersion; var ConsolidatedEntry = class { constructor(text, version) { this.text = text; this.version = version; } toString() { return this.text; } toNumber() { var _a; return parseFloat((_a = this.version) !== null && _a !== void 0 ? _a : ""); } [Symbol.toPrimitive](hint) { if (hint === "number") { return this.toNumber(); } return this.toString(); } }; exports.ConsolidatedEntry = ConsolidatedEntry; var Changelog = class _Changelog { constructor() { this.versions = []; } /** * Filter versions in current changelog and return a new changelog * * @param callback callback function to filter versions */ filter(callback) { const result = new _Changelog(); result.title = this.title; result.description = this.description; result.versions = this.versions.filter(callback); return result; } consolidate() { const logByType = {}; this.versions.forEach((version) => { Object.entries(version.parsed).filter(([key]) => key !== "_").forEach((entry) => { const [type, texts] = entry; if (!logByType[type]) { logByType[type] = []; } logByType[type].push(...texts.map((text) => new ConsolidatedEntry(text, version.version))); }); }); return logByType; } }; exports.Changelog = Changelog; var OpenChangelogVersion = class extends ChangelogVersion { constructor() { super(...arguments); this._private = { activeSubhead: null }; } }; exports.OpenChangelogVersion = OpenChangelogVersion; var ProcessingContext = class { constructor() { this.log = new Changelog(); this.current = null; this.lineCounter = 0; } }; exports.ProcessingContext = ProcessingContext; } }); // node_modules/.pnpm/remove-markdown@0.5.5/node_modules/remove-markdown/index.js var require_remove_markdown = __commonJS({ "node_modules/.pnpm/remove-markdown@0.5.5/node_modules/remove-markdown/index.js"(exports, module) { module.exports = function(md, options) { options = options || {}; options.listUnicodeChar = options.hasOwnProperty("listUnicodeChar") ? options.listUnicodeChar : false; options.stripListLeaders = options.hasOwnProperty("stripListLeaders") ? options.stripListLeaders : true; options.gfm = options.hasOwnProperty("gfm") ? options.gfm : true; options.useImgAltText = options.hasOwnProperty("useImgAltText") ? options.useImgAltText : true; options.abbr = options.hasOwnProperty("abbr") ? options.abbr : false; options.replaceLinksWithURL = options.hasOwnProperty("replaceLinksWithURL") ? options.replaceLinksWithURL : false; options.htmlTagsToSkip = options.hasOwnProperty("htmlTagsToSkip") ? options.htmlTagsToSkip : []; options.throwError = options.hasOwnProperty("throwError") ? options.throwError : false; var output = md || ""; output = output.replace(/^(-\s*?|\*\s*?|_\s*?){3,}\s*/gm, ""); try { if (options.stripListLeaders) { if (options.listUnicodeChar) output = output.replace(/^([\s\t]*)([\*\-\+]|\d+\.)\s+/gm, options.listUnicodeChar + " $1"); else output = output.replace(/^([\s\t]*)([\*\-\+]|\d+\.)\s+/gm, "$1"); } if (options.gfm) { output = output.replace(/\n={2,}/g, "\n").replace(/~{3}.*\n/g, "").replace(/~~/g, "").replace(/`{3}.*\n/g, ""); } if (options.abbr) { output = output.replace(/\*\[.*\]:.*\n/, ""); } output = output.replace(/<[^>]*>/g, ""); var htmlReplaceRegex = new RegExp("<[^>]*>", "g"); if (options.htmlTagsToSkip.length > 0) { var joinedHtmlTagsToSkip = "(?!" + options.htmlTagsToSkip.join("|") + ")"; htmlReplaceRegex = new RegExp( "<" + joinedHtmlTagsToSkip + "[^>]*>", "ig" ); } output = output.replace(htmlReplaceRegex, "").replace(/^[=\-]{2,}\s*$/g, "").replace(/\[\^.+?\](\: .*?$)?/g, "").replace(/\s{0,2}\[.*?\]: .*?$/g, "").replace(/\!\[(.*?)\][\[\(].*?[\]\)]/g, options.useImgAltText ? "$1" : "").replace(/\[([^\]]*?)\][\[\(].*?[\]\)]/g, options.replaceLinksWithURL ? "$2" : "$1").replace(/^(\n)?\s{0,3}>\s?/gm, "$1").replace(/^\s{1,2}\[(.*?)\]: (\S+)( ".*?")?\s*$/g, "").replace(/^(\n)?\s{0,}#{1,6}\s*( (.+))? +#+$|^(\n)?\s{0,}#{1,6}\s*( (.+))?$/gm, "$1$3$4$6").replace(/([\*]+)(\S)(.*?\S)??\1/g, "$2$3").replace(/(^|\W)([_]+)(\S)(.*?\S)??\2($|\W)/g, "$1$3$4$5").replace(/(`{3,})(.*?)\1/gm, "$2").replace(/`(.+?)`/g, "$1").replace(/~(.*?)~/g, "$1"); } catch (e) { if (options.throwError) throw e; console.error("remove-markdown encountered error: %s", e); return md; } return output; }; } }); // lib/cjs/lib/parser.js var require_parser = __commonJS({ "lib/cjs/lib/parser.js"(exports) { "use strict"; var __importDefault = exports && exports.__importDefault || function(mod) { return mod && mod.__esModule ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ChangelogParser = void 0; var remove_markdown_1 = __importDefault(require_remove_markdown()); var types_1 = require_types(); var ChangelogParser2 = class _ChangelogParser { get currentLine() { var _a, _b; return (_b = (_a = this.state) === null || _a === void 0 ? void 0 : _a.lineCounter) !== null && _b !== void 0 ? _b : 0; } constructor(options = {}) { this.options = Object.assign(Object.assign(Object.assign({}, _ChangelogParser.defaultOptions), options), { patterns: Object.assign(Object.assign({}, _ChangelogParser.defaultOptions.patterns), options.patterns) }); } /** * Initializes the parsing process. */ start() { var _a, _b; this.state = { log: new types_1.Changelog(), current: null, lineCounter: 0 }; if (this.options.text && this.options.newline === types_1.NewlineSymbol.AUTO) { this.options.newline = (_b = (_a = RegExp(this.options.patterns.eol).exec(this.options.text)) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : "\n"; } } /** * Finalizes the parsing process and returns the result. * * @returns {Changelog} parsed changelog object */ end() { if (!this.state) throw new Error("Parser not started"); if (this.state.current) { this.pushCurrent(); } this.state.log.description = this.clean(this.state.log.description); if (this.state.log.description === "") delete this.state.log.description; this.lastResult = this.state.log; return this.lastResult; } /** * Calls start, splits text into lines, and calls handleLine for each line. * Calls end and returns the result. * * @returns {Object} parsed changelog object */ parse(text) { var _a, _b; this.start(); if (text) { this.options.text = text; } if (!this.options.text) return (_b = (_a = this.state) === null || _a === void 0 ? void 0 : _a.log) !== null && _b !== void 0 ? _b : new types_1.Changelog(); this.options.text.split(new RegExp(this.options.patterns.eol, "gm")).forEach((line) => this.handleLine(line)); return this.end(); } /** * Feed the parser one line of changelog. * * @param line - next line of text from the changelog file * @returns */ handleLine(line) { var _a, _b, _c, _d; if (!this.state) throw new Error("Parser not started"); this.state.lineCounter++; if (this.options.patterns.linkLabel.exec(line)) return; if (!this.state.log.title && this.options.patterns.title.exec(line)) { this.state.log.title = line.substring(1).trim(); return; } if (this.options.patterns.versionTitle.exec(line)) { if ((_a = this.state.current) === null || _a === void 0 ? void 0 : _a.title) this.pushCurrent(); this.parseVersionTitle(line); return; } if (this.state.current) { this.state.current.body += line + this.options.newline; let isListItem = false; if (this.options.patterns.subhead.exec(line)) { const key = line.replace("###", "").trim(); if (!this.state.current.parsed[key]) { this.state.current.parsed[key] = []; } if (!this.state.current._private) this.state.current._private = {}; this.state.current._private.activeSubhead = key; } else if (this.options.patterns.listItem.exec(line)) { isListItem = true; const log = this.options.removeMarkdown ? (0, remove_markdown_1.default)(line) : line; this.state.current.parsed._.push(log); if ((_b = this.state.current._private) === null || _b === void 0 ? void 0 : _b.activeSubhead) { this.state.current.parsed[this.state.current._private.activeSubhead].push(log); } } else if (((_c = this.state.current._private) === null || _c === void 0 ? void 0 : _c.activeSubhead) && line.trim() !== "" && this.state.current._private.isListItem) { isListItem = true; const currentList = this.state.current.parsed[this.state.current._private.activeSubhead]; currentList[currentList.length - 1] += this.options.newline + line; } else if (line.trim() !== "") { this.state.current.description = this.state.current.description ? this.state.current.description + this.options.newline + line : line; } if (this.state.current._private) this.state.current._private.isListItem = isListItem; } else { this.state.log.description = ((_d = this.state.log.description) !== null && _d !== void 0 ? _d : "") + line + this.options.newline; } } parseVersionTitle(line) { if (!this.state) throw new Error("Parser not started"); this.state.current = new types_1.ChangelogVersion(); this.state.current.line = this.state.lineCounter; const matchResult = this.options.patterns.semver.exec(line); if (matchResult) this.state.current.version = matchResult[1]; this.state.current.title = line.substring(2).trim(); const dateMatch = this.options.patterns.date.exec(this.state.current.title); if (this.state.current.title && dateMatch) this.state.current.date = dateMatch[1]; } pushCurrent() { var _a; if (!((_a = this.state) === null || _a === void 0 ? void 0 : _a.current)) throw new Error("Parser not started"); delete this.state.current._private; this.state.current.body = this.clean(this.state.current.body); this.state.log.versions.push(this.state.current); } clean(str) { if (!str) return ""; const newlinePattern = this.options.patterns.eol.source; str = str.trim(); str = str.replace(new RegExp(`^(${newlinePattern})*`), ""); str = str.replace(new RegExp(`(${newlinePattern})*$`), ""); str = str.replace(new RegExp(newlinePattern, "g"), this.options.newline); return str; } }; exports.ChangelogParser = ChangelogParser2; ChangelogParser2.defaultOptions = { text: "", removeMarkdown: true, newline: "\n", patterns: { semver: /\[?v?([\w\d.-]+\.[\w\d.-]+[a-zA-Z0-9])\]?/, date: /.* \(?(\d\d?\d?\d?[-/.]\d\d?[-/.]\d\d?\d?\d?)\)?.*/, subhead: /^###/, listItem: /^\s{0,3}[*-]/, linkLabel: /^\[[^[\]]*\] *?:/, title: /^# ?[^#]/, versionTitle: /^## ?[^#]/, eol: /\r\n?|\n/ } }; } }); // lib/cjs/index.js var require_cjs = __commonJS({ "lib/cjs/index.js"(exports) { "use strict"; var __createBinding = exports && exports.__createBinding || (Object.create ? function(o, m, k, k2) { if (k2 === void 0) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); } : function(o, m, k, k2) { if (k2 === void 0) k2 = k; o[k2] = m[k]; }); var __exportStar = exports && exports.__exportStar || function(m, exports2) { for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports2, p)) __createBinding(exports2, m, p); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ChangelogParser = void 0; __exportStar(require_types(), exports); var parser_1 = require_parser(); Object.defineProperty(exports, "ChangelogParser", { enumerable: true, get: function() { return parser_1.ChangelogParser; } }); } }); // examples/example-changelogs/keepachangelog.md var require_keepachangelog = __commonJS({ "examples/example-changelogs/keepachangelog.md"(exports, module) { module.exports = `<!-- This is an example of a CHANGELOG.md file that is used to test the package. This file is based on the Keep a Changelog project's CHANGELOG.md file. --> # Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] ### Added - v1.1 Brazilian Portuguese translation. - v1.1 German Translation - v1.1 Spanish translation. - v1.1 Italian translation. - v1.1 Polish translation. - v1.1 Ukrainian translation. ### Changed - Use frontmatter title & description in each language version template - Replace broken OpenGraph image with an appropriately-sized Keep a Changelog image that will render properly (although in English for all languages) - Fix OpenGraph title & description for all languages so the title and description when links are shared are language-appropriate ### Removed - Trademark sign previously shown after the project description in version 0.3.0 ## [1.1.1] - 2023-03-05 ### Added - Arabic translation (#444). - v1.1 French translation. - v1.1 Dutch translation (#371). - v1.1 Russian translation (#410). - v1.1 Japanese translation (#363). - v1.1 Norwegian Bokm\xE5l translation (#383). - v1.1 "Inconsistent Changes" Turkish translation (#347). - Default to most recent versions available for each languages. - Display count of available translations (26 to date!). - Centralize all links into \\\`/data/links.json\\\` so they can be updated easily. ### Fixed - Improve French translation (#377). - Improve id-ID translation (#416). - Improve Persian translation (#457). - Improve Russian translation (#408). - Improve Swedish title (#419). - Improve zh-CN translation (#359). - Improve French translation (#357). - Improve zh-TW translation (#360, #355). - Improve Spanish (es-ES) transltion (#362). - Foldout menu in Dutch translation (#371). - Missing periods at the end of each change (#451). - Fix missing logo in 1.1 pages. - Display notice when translation isn't for most recent version. - Various broken links, page versions, and indentations. ### Changed - Upgrade dependencies: Ruby 3.2.1, Middleman, etc. ### Removed - Unused normalize.css file. - Identical links assigned in each translation file. - Duplicate index file for the english version. ## [1.1.0] - 2019-02-15 ### Added - Danish translation (#297). - Georgian translation from (#337). - Changelog inconsistency section in Bad Practices. ### Fixed - Italian translation (#332). - Indonesian translation (#336). ## [1.0.0] - 2017-06-20 ### Added - New visual identity by [@tylerfortune8](https://github.com/tylerfortune8). - Version navigation. - Links to latest released version in previous versions. - "Why keep a changelog?" section. - "Who needs a changelog?" section. - "How do I make a changelog?" section. - "Frequently Asked Questions" section. - New "Guiding Principles" sub-section to "How do I make a changelog?". - Simplified and Traditional Chinese translations from [@tianshuo](https://github.com/tianshuo). - German translation from [@mpbzh](https://github.com/mpbzh) & [@Art4](https://github.com/Art4). - Italian translation from [@azkidenz](https://github.com/azkidenz). - Swedish translation from [@magol](https://github.com/magol). - Turkish translation from [@emreerkan](https://github.com/emreerkan). - French translation from [@zapashcanon](https://github.com/zapashcanon). - Brazilian Portuguese translation from [@Webysther](https://github.com/Webysther). - Polish translation from [@amielucha](https://github.com/amielucha) & [@m-aciek](https://github.com/m-aciek). - Russian translation from [@aishek](https://github.com/aishek). - Czech translation from [@h4vry](https://github.com/h4vry). - Slovak translation from [@jkostolansky](https://github.com/jkostolansky). - Korean translation from [@pierceh89](https://github.com/pierceh89). - Croatian translation from [@porx](https://github.com/porx). - Persian translation from [@Hameds](https://github.com/Hameds). - Ukrainian translation from [@osadchyi-s](https://github.com/osadchyi-s). ### Changed - Start using "changelog" over "change log" since it's the common usage. - Start versioning based on the current English version at 0.3.0 to help translation authors keep things up-to-date. - Rewrite "What makes unicorns cry?" section. - Rewrite "Ignoring Deprecations" sub-section to clarify the ideal scenario. - Improve "Commit log diffs" sub-section to further argument against them. - Merge "Why can\u2019t people just use a git log diff?" with "Commit log diffs". - Fix typos in Simplified Chinese and Traditional Chinese translations. - Fix typos in Brazilian Portuguese translation. - Fix typos in Turkish translation. - Fix typos in Czech translation. - Fix typos in Swedish translation. - Improve phrasing in French translation. - Fix phrasing and spelling in German translation. ### Removed - Section about "changelog" vs "CHANGELOG". ## [0.3.0] - 2015-12-03 ### Added - RU translation from [@aishek](https://github.com/aishek). - pt-BR translation from [@tallesl](https://github.com/tallesl). - es-ES translation from [@ZeliosAriex](https://github.com/ZeliosAriex). ## [0.2.0] - 2015-10-06 ### Changed - Remove exclusionary mentions of "open source" since this project can benefit both "open" and "closed" source projects equally. ## [0.1.0] - 2015-10-06 ### Added - Answer "Should you ever rewrite a change log?". ### Changed - Improve argument against commit logs. - Start following [SemVer](https://semver.org) properly. ## [0.0.8] - 2015-02-17 ### Changed - Update year to match in every README example. - Reluctantly stop making fun of Brits only, since most of the world writes dates in a strange way. ### Fixed - Fix typos in recent README changes. - Update outdated unreleased diff link. ## [0.0.7] - 2015-02-16 ### Added - Link, and make it obvious that date format is ISO 8601. ### Changed - Clarified the section on "Is there a standard change log format?". ### Fixed - Fix Markdown links to tag comparison URL with footnote-style links. ## [0.0.6] - 2014-12-12 ### Added - README section on "yanked" releases. ## [0.0.5] - 2014-08-09 ### Added - Markdown links to version tags on release headings. - Unreleased section to gather unreleased changes and encourage note keeping prior to releases. ## [0.0.4] - 2014-08-09 ### Added - Better explanation of the difference between the file ("CHANGELOG") and its function "the change log". ### Changed - Refer to a "change log" instead of a "CHANGELOG" throughout the site to differentiate between the file and the purpose of the file \u2014 the logging of changes. ### Removed - Remove empty sections from CHANGELOG, they occupy too much space and create too much noise in the file. People will have to assume that the missing sections were intentionally left out because they contained no notable changes. ## [0.0.3] - 2014-08-09 ### Added - "Why should I care?" section mentioning The Changelog podcast. ## [0.0.2] - 2014-07-10 ### Added - Explanation of the recommended reverse chronological release ordering. ## [0.0.1] - 2014-05-31 ### Added - This CHANGELOG file to hopefully serve as an evolving example of a standardized open source project CHANGELOG. - CNAME file to enable GitHub Pages custom domain. - README now contains answers to common questions about CHANGELOGs. - Good examples and basic guidelines, including proper date formatting. - Counter-examples: "What makes unicorns cry?". [unreleased]: https://github.com/olivierlacan/keep-a-changelog/compare/v1.1.1...HEAD [1.1.1]: https://github.com/olivierlacan/keep-a-changelog/compare/v1.1.0...v1.1.1 [1.1.0]: https://github.com/olivierlacan/keep-a-changelog/compare/v1.0.0...v1.1.0 [1.0.0]: https://github.com/olivierlacan/keep-a-changelog/compare/v0.3.0...v1.0.0 [0.3.0]: https://github.com/olivierlacan/keep-a-changelog/compare/v0.2.0...v0.3.0 [0.2.0]: https://github.com/olivierlacan/keep-a-changelog/compare/v0.1.0...v0.2.0 [0.1.0]: https://github.com/olivierlacan/keep-a-changelog/compare/v0.0.8...v0.1.0 [0.0.8]: https://github.com/olivierlacan/keep-a-changelog/compare/v0.0.7...v0.0.8 [0.0.7]: https://github.com/olivierlacan/keep-a-changelog/compare/v0.0.6...v0.0.7 [0.0.6]: https://github.com/olivierlacan/keep-a-changelog/compare/v0.0.5...v0.0.6 [0.0.5]: https://github.com/olivierlacan/keep-a-changelog/compare/v0.0.4...v0.0.5 [0.0.4]: https://github.com/olivierlacan/keep-a-changelog/compare/v0.0.3...v0.0.4 [0.0.3]: https://github.com/olivierlacan/keep-a-changelog/compare/v0.0.2...v0.0.3 [0.0.2]: https://github.com/olivierlacan/keep-a-changelog/compare/v0.0.1...v0.0.2 [0.0.1]: https://github.com/olivierlacan/keep-a-changelog/releases/tag/v0.0.1`; } }); // examples/simple-browser/index.js var { ChangelogParser } = require_cjs(); var exampleLog = require_keepachangelog(); var changelog = new ChangelogParser({ text: exampleLog }).parse(); console.log(changelog); document.getElementById("changelog-out").innerHTML = JSON.stringify( changelog, null, 2 ); document.getElementById("source-out").innerHTML = exampleLog; })();