From fd1a5b88efd47cb5b529f85a3974344b274d76d8 Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Thu, 29 Oct 2020 23:34:33 +0100 Subject: [PATCH 1/3] Adjust test expectations Version headings in lerna-changelog are h2, not h3 --- test.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/test.js b/test.js index 20bbe2f..66dfe4f 100644 --- a/test.js +++ b/test.js @@ -53,7 +53,7 @@ class TestPlugin extends Plugin { // always assume v1.0.0 unless specifically overridden 'git describe --tags --abbrev=0': 'v1.0.0', - [`${LERNA_PATH} --next-version=Unreleased --from=v1.0.0`]: '### Unreleased (2020-03-18)\n\nThe changelog', + [`${LERNA_PATH} --next-version=Unreleased --from=v1.0.0`]: '## Unreleased (2020-03-18)\n\nThe changelog', }; this.commands = []; @@ -110,7 +110,7 @@ test('it honors custom git.tagName formatting', async (t) => { ]); const changelog = fs.readFileSync(infile, { encoding: 'utf8' }); - t.is(changelog, `### v1.0.1 (2020-03-18)\n\nThe changelog\n\n`); + t.is(changelog, `## v1.0.1 (2020-03-18)\n\nThe changelog\n\n`); }); test('it sets the changelog without version information onto the config', async (t) => { @@ -147,7 +147,7 @@ test('it uses the first commit when no tags exist', async (t) => { value: 'hahahahaah, does not exist', }, 'git rev-list --max-parents=0 HEAD': 'aabc', - [`${LERNA_PATH} --next-version=Unreleased --from=aabc`]: `### Unreleased\n\nThe changelog\n### v1.0.0\n\nThe old changelog`, + [`${LERNA_PATH} --next-version=Unreleased --from=aabc`]: `## Unreleased\n\nThe changelog\n## v1.0.0\n\nThe old changelog`, }); await runTasks(plugin); @@ -159,7 +159,7 @@ test('it uses the first commit when no tags exist', async (t) => { ]); const changelog = fs.readFileSync(infile, { encoding: 'utf8' }); - t.is(changelog.trim(), '### v1.0.1\n\nThe changelog\n### v1.0.0\n\nThe old changelog'); + t.is(changelog.trim(), '## v1.0.1\n\nThe changelog\n## v1.0.0\n\nThe old changelog'); }); test('it writes the changelog to the specified file when it did not exist', async (t) => { @@ -171,7 +171,7 @@ test('it writes the changelog to the specified file when it did not exist', asyn Object.assign(plugin.responses, { 'git rev-list --max-parents=0 HEAD': 'aabc', - [`${LERNA_PATH} --next-version=Unreleased --from=aabc`]: `### Unreleased\n\nThe changelog\n### v1.0.0\n\nThe old changelog`, + [`${LERNA_PATH} --next-version=Unreleased --from=aabc`]: `## Unreleased\n\nThe changelog\n## v1.0.0\n\nThe old changelog`, }); await runTasks(plugin); @@ -185,7 +185,7 @@ test('it writes the changelog to the specified file when it did not exist', asyn ]); const changelog = fs.readFileSync(infile, { encoding: 'utf8' }); - t.is(changelog.trim(), '### v1.0.1\n\nThe changelog\n### v1.0.0\n\nThe old changelog'); + t.is(changelog.trim(), '## v1.0.1\n\nThe changelog\n## v1.0.0\n\nThe old changelog'); }); test('prepends the changelog to the existing file', async (t) => { @@ -198,7 +198,7 @@ test('prepends the changelog to the existing file', async (t) => { await runTasks(plugin); const changelog = fs.readFileSync(infile); - t.is(changelog.toString().trim(), '### v1.0.1 (2020-03-18)\n\nThe changelog\n\nOld contents'); + t.is(changelog.toString().trim(), '## v1.0.1 (2020-03-18)\n\nThe changelog\n\nOld contents'); }); test('uses launchEditor command', async (t) => { @@ -313,7 +313,7 @@ test('launches configured editor, updates infile, and propogates changes to cont const changelogFileContents = fs.readFileSync(infile); t.is( changelogFileContents.toString().trim(), - '### v1.0.1 (2020-03-18)\n\nThe changelog\nExtra stuff!' + '## v1.0.1 (2020-03-18)\n\nThe changelog\nExtra stuff!' ); const { changelog } = plugin.config.getContext(); From fc865f3eb860f91661dad0e9b3c28b2bc5541f2b Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Thu, 29 Oct 2020 23:15:28 +0100 Subject: [PATCH 2/3] Extract `_prependChangelog()` method --- index.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index d131630..12951b4 100644 --- a/index.js +++ b/index.js @@ -163,7 +163,8 @@ module.exports = class LernaChangelogGeneratorPlugin extends Plugin { this.log.log(`! Prepending ${infile} with release notes.`); } else { let currentFileData = hasInfile ? fs.readFileSync(infile, { encoding: 'utf8' }) : ''; - fs.writeFileSync(infile, changelog + EOL + EOL + currentFileData, { encoding: 'utf8' }); + let newContent = this._insertContent(changelog, currentFileData); + fs.writeFileSync(infile, newContent, { encoding: 'utf8' }); } if (!hasInfile) { @@ -171,6 +172,10 @@ module.exports = class LernaChangelogGeneratorPlugin extends Plugin { } } + _insertContent(newContent, oldContent) { + return newContent + EOL + EOL + oldContent; + } + async beforeRelease() { let processedChangelog = await this.processChangelog(); From e944c671ded309d660c0286f8233733cb4f8119d Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Thu, 29 Oct 2020 23:43:43 +0100 Subject: [PATCH 3/3] Insert new changelog content before the first h2 existing element ... or at the start of the file if none is found --- index.js | 12 ++++++- package.json | 1 + test.js | 16 +++++++++ yarn.lock | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 121 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 12951b4..5fdbcec 100644 --- a/index.js +++ b/index.js @@ -7,6 +7,7 @@ const { Plugin } = require('release-it'); const { format } = require('release-it/lib/util'); const tmp = require('tmp'); const execa = require('execa'); +const parse = require('mdast-util-from-markdown'); require('validate-peer-dependencies')(__dirname); @@ -173,7 +174,16 @@ module.exports = class LernaChangelogGeneratorPlugin extends Plugin { } _insertContent(newContent, oldContent) { - return newContent + EOL + EOL + oldContent; + let insertOffset = this._findInsertOffset(oldContent); + let before = oldContent.slice(0, insertOffset); + let after = oldContent.slice(insertOffset); + return before + newContent + EOL + EOL + after; + } + + _findInsertOffset(oldContent) { + let ast = parse(oldContent); + let firstH2 = ast.children.find((it) => it.type === 'heading' && it.depth === 2); + return firstH2 ? firstH2.position.start.offset : 0; } async beforeRelease() { diff --git a/package.json b/package.json index 58f45c9..e3afeec 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "dependencies": { "execa": "^4.0.3", "lerna-changelog": "^1.0.1", + "mdast-util-from-markdown": "^0.8.1", "tmp": "^0.2.1", "validate-peer-dependencies": "^1.0.0", "which": "^2.0.2" diff --git a/test.js b/test.js index 66dfe4f..2b92155 100644 --- a/test.js +++ b/test.js @@ -201,6 +201,22 @@ test('prepends the changelog to the existing file', async (t) => { t.is(changelog.toString().trim(), '## v1.0.1 (2020-03-18)\n\nThe changelog\n\nOld contents'); }); +test('adds the changelog after an existing first level heading', async (t) => { + let infile = tmp.fileSync().name; + let plugin = buildPlugin({ infile }); + plugin.config.setContext({ git: { tagName: 'v${version}' } }); + + fs.writeFileSync(infile, '# Changelog\n\n## v1.0.0\n\nThe old changelog', { encoding: 'utf8' }); + + await runTasks(plugin); + + const changelog = fs.readFileSync(infile); + t.is( + changelog.toString().trim(), + '# Changelog\n\n## v1.0.1 (2020-03-18)\n\nThe changelog\n\n## v1.0.0\n\nThe old changelog' + ); +}); + test('uses launchEditor command', async (t) => { let infile = tmp.fileSync().name; diff --git a/yarn.lock b/yarn.lock index 9928964..70d180c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -318,6 +318,13 @@ dependencies: "@types/node" "*" +"@types/mdast@^3.0.0": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-3.0.3.tgz#2d7d671b1cd1ea3deb306ea75036c2a0407d2deb" + integrity sha512-SXPBMnFVQg1s00dlMCc/jCdvPqdE4mXaMMCeRlxLDmTAEoegHT53xKtkDnzDTOcmMHUfcjyf36/YYZ6SxRdnsw== + dependencies: + "@types/unist" "*" + "@types/minimatch@*": version "3.0.3" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" @@ -345,6 +352,11 @@ dependencies: "@types/node" "*" +"@types/unist@*": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.3.tgz#9c088679876f374eb5983f150d4787aa6fb32d7e" + integrity sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ== + acorn-jsx@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.2.0.tgz#4c66069173d6fdd68ed85239fc256226182b2ebe" @@ -731,6 +743,21 @@ chalk@^3.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" +character-entities-legacy@^1.0.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz#94bc1845dce70a5bb9d2ecc748725661293d8fc1" + integrity sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA== + +character-entities@^1.0.0: + version "1.2.4" + resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-1.2.4.tgz#e12c3939b7eaf4e5b15e7ad4c5e28e1d48c5b16b" + integrity sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw== + +character-reference-invalid@^1.0.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz#083329cda0eae272ab3dbbf37e9a382c13af1560" + integrity sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg== + chardet@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" @@ -1021,6 +1048,13 @@ debug@4, debug@4.1.1, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: dependencies: ms "^2.1.1" +debug@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.2.0.tgz#7f150f93920e94c58f5574c2fd01a3110effe7f1" + integrity sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg== + dependencies: + ms "2.1.2" + decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" @@ -1899,6 +1933,19 @@ irregular-plurals@^3.2.0: resolved "https://registry.yarnpkg.com/irregular-plurals/-/irregular-plurals-3.2.0.tgz#b19c490a0723798db51b235d7e39add44dab0822" integrity sha512-YqTdPLfwP7YFN0SsD3QUVCkm9ZG2VzOXv3DOrw5G5mkMbVwptTwVcFv7/C0vOpBmgTxAeTG19XpUs1E522LW9Q== +is-alphabetical@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-1.0.4.tgz#9e7d6b94916be22153745d184c298cbf986a686d" + integrity sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg== + +is-alphanumerical@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz#7eb9a2431f855f6b1ef1a78e326df515696c4dbf" + integrity sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A== + dependencies: + is-alphabetical "^1.0.0" + is-decimal "^1.0.0" + is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" @@ -1925,6 +1972,11 @@ is-core-module@^2.0.0: dependencies: has "^1.0.3" +is-decimal@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-1.0.4.tgz#65a3a5958a1c5b63a706e1b333d7cd9f630d3fa5" + integrity sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw== + is-error@^2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/is-error/-/is-error-2.2.2.tgz#c10ade187b3c93510c5470a5567833ee25649843" @@ -1952,6 +2004,11 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" +is-hexadecimal@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz#cc35c97588da4bd49a8eedd6bc4082d44dcb23a7" + integrity sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw== + is-installed-globally@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.3.1.tgz#679afef819347a72584617fd19497f010b8ed35f" @@ -2320,6 +2377,21 @@ md5-o-matic@^0.1.1: resolved "https://registry.yarnpkg.com/md5-o-matic/-/md5-o-matic-0.1.1.tgz#822bccd65e117c514fab176b25945d54100a03c3" integrity sha1-givM1l4RfFFPqxdrJZRdVBAKA8M= +mdast-util-from-markdown@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/mdast-util-from-markdown/-/mdast-util-from-markdown-0.8.1.tgz#781371d493cac11212947226190270c15dc97116" + integrity sha512-qJXNcFcuCSPqUF0Tb0uYcFDIq67qwB3sxo9RPdf9vG8T90ViKnksFqdB/Coq2a7sTnxL/Ify2y7aIQXDkQFH0w== + dependencies: + "@types/mdast" "^3.0.0" + mdast-util-to-string "^1.0.0" + micromark "~2.10.0" + parse-entities "^2.0.0" + +mdast-util-to-string@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-1.1.0.tgz#27055500103f51637bd07d01da01eb1967a43527" + integrity sha512-jVU0Nr2B9X3MU4tSK7JP1CMkSvOj7X5l/GboG1tKRw52lLF1x2Ju92Ms9tNetCcbfX3hzlM73zYo2NKkWSfF/A== + mem@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/mem/-/mem-6.1.0.tgz#846eca0bd4708a8f04b9c3f3cd769e194ae63c5c" @@ -2338,6 +2410,14 @@ merge2@^1.2.3, merge2@^1.3.0: resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.3.0.tgz#5b366ee83b2f1582c48f87e47cf1a9352103ca81" integrity sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw== +micromark@~2.10.0: + version "2.10.1" + resolved "https://registry.yarnpkg.com/micromark/-/micromark-2.10.1.tgz#cd73f54e0656f10e633073db26b663a221a442a7" + integrity sha512-fUuVF8sC1X7wsCS29SYQ2ZfIZYbTymp0EYr6sab3idFjigFFjGa5UwoniPlV9tAgntjuapW1t9U+S0yDYeGKHQ== + dependencies: + debug "^4.0.0" + parse-entities "^2.0.0" + micromatch@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259" @@ -2474,7 +2554,7 @@ move-concurrently@^1.0.1: rimraf "^2.5.4" run-queue "^1.0.3" -ms@^2.0.0, ms@^2.1.1, ms@^2.1.2: +ms@2.1.2, ms@^2.0.0, ms@^2.1.1, ms@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== @@ -2734,6 +2814,18 @@ parent-module@^1.0.0: dependencies: callsites "^3.0.0" +parse-entities@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-2.0.0.tgz#53c6eb5b9314a1f4ec99fa0fdf7ce01ecda0cbe8" + integrity sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ== + dependencies: + character-entities "^1.0.0" + character-entities-legacy "^1.0.0" + character-reference-invalid "^1.0.0" + is-alphanumerical "^1.0.0" + is-decimal "^1.0.0" + is-hexadecimal "^1.0.0" + parse-json@5.1.0, parse-json@^5.0.0: version "5.1.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.1.0.tgz#f96088cdf24a8faa9aea9a009f2d9d942c999646"