Skip to content

Commit a770ba8

Browse files
authored
just dont check previous, add another validation check (#36)
* just dont check previous, add another validation check * add period * fix array.from brackets * didnt get strictLatest, remove option * add tests, order check, many unreleased (disallowed) check * clarify, must be valid for greater than
1 parent dff5391 commit a770ba8

File tree

4 files changed

+45
-31
lines changed

4 files changed

+45
-31
lines changed

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ In [release-it](https://github.com/release-it/release-it) config:
3131
| option | default value | description |
3232
| ----------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
3333
| filename | `'CHANGELOG.md'` | File with changelogs. |
34-
| strictLatest | `true` | Entry of latest version must be present in order to get correct changelog. Set this option to `false` if you expect latest version without logs. |
3534
| addUnreleased | `false` | It leaves "Unreleased" title row if set to `true`. |
3635
| keepUnreleased | `false` | It leaves "Unreleased" title row unchanged if set to `true`. |
3736
| addVersionUrl | `false` | Links the version to the according changeset. Uses GitHub-compatible URLs by default, see other options to configure the URL format. |

index.js

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import fs from 'fs';
33
import path from 'path';
44
import { detectNewline } from 'detect-newline';
55
import format from 'string-template';
6+
import semver from "semver";
67

78
const pad = num => ('0' + num).slice(-2);
89

@@ -28,10 +29,9 @@ const defaultVersionUrlFormats = {
2829
class KeepAChangelog extends Plugin {
2930
async init() {
3031
await super.init();
31-
const { filename, strictLatest, addUnreleased, keepUnreleased, addVersionUrl, versionUrlFormats, head } = this.options;
32+
const { filename, addUnreleased, keepUnreleased, addVersionUrl, versionUrlFormats, head } = this.options;
3233

3334
this.filename = filename || 'CHANGELOG.md';
34-
this.strictLatest = strictLatest === undefined ? true : Boolean(strictLatest);
3535
this.addUnreleased = addUnreleased === undefined ? false : Boolean(addUnreleased);
3636
this.keepUnreleased = keepUnreleased === undefined ? false : Boolean(keepUnreleased);
3737
this.addVersionUrl = addVersionUrl === undefined ? false : Boolean(addVersionUrl);
@@ -56,15 +56,41 @@ class KeepAChangelog extends Plugin {
5656
const { changelog } = this.getContext();
5757
if (changelog) return changelog;
5858

59-
const { filename, strictLatest } = this;
60-
const previousReleaseTitle = strictLatest ? `## [${latestVersion}]` : `## [`;
61-
const hasPreviousReleaseSection = this.changelogContent.includes(previousReleaseTitle);
62-
if (strictLatest && !hasPreviousReleaseSection) {
63-
throw Error(`Missing section for previous release ("${latestVersion}") in ${filename}.`);
59+
const unreleasedTitleRawList = Array.from(this.changelogContent.matchAll(/(?<=## \[)Unreleased(?=])/g)).map(ver => ver?.[0])
60+
if(unreleasedTitleRawList.length > 1) {
61+
throw new Error(`Too many "Unreleased" sections in ${this.filename}: ${unreleasedTitleRawList.length}.`)
62+
}
63+
const versionTitleRawList = Array.from(this.changelogContent.matchAll(/(?<=## \[)((\d+\.){2}\d+[^\]]*)/g)).map(ver => ver?.[0])
64+
const badTitleRawList = versionTitleRawList.filter(ver => {
65+
// invalid: ver > latestVersion
66+
return !semver.valid(ver) || semver.gt(ver, latestVersion);
67+
})
68+
if(badTitleRawList.length > 0) {
69+
throw new Error(`Invalid versions in ${this.filename}: ${badTitleRawList.join(', ')}. Current: ${latestVersion}.`)
70+
}
71+
const unorderedTitleRawList = versionTitleRawList.filter((ver, veri) => {
72+
const previous = versionTitleRawList[veri - 1];
73+
const next = versionTitleRawList[veri + 1];
74+
75+
if(previous) {
76+
// bad order: ver > previous
77+
return semver.gt(ver, previous)
78+
}
79+
80+
if(next) {
81+
// bad order: ver < next
82+
return semver.lt(ver, next)
83+
}
84+
85+
return false
86+
})
87+
if(unorderedTitleRawList.length > 0) {
88+
throw new Error(`Invalid sections order in ${this.filename}: ${unorderedTitleRawList.join(', ')}.`)
6489
}
6590

6691
const { isIncrement } = this.config;
67-
const titleToFind = isIncrement ? this.unreleasedTitleRaw : latestVersion;
92+
// use unreleased if initial release
93+
const titleToFind = isIncrement || versionTitleRawList.length === 0 ? this.unreleasedTitleRaw : latestVersion;
6894
const changelogContent = this.getChangelogEntryContent(titleToFind);
6995

7096
this.setContext({ changelog: changelogContent });

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
},
3232
"dependencies": {
3333
"detect-newline": "^4.0.1",
34+
"semver": "^7.6.3",
3435
"string-template": "^1.0.0"
3536
},
3637
"devDependencies": {

test.js

Lines changed: 10 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ const initialDryRunFileContents =
1212

1313
vol.fromJSON({
1414
'./CHANGELOG-FOO.md': '## [FOO]\n\n* Item A\n* Item B',
15-
'./CHANGELOG-MISSING.md': '## [Unreleased]\n\n* Item A\n* Item B',
15+
'./CHANGELOG-MULTIPLE_UNRELEASED.md': '## [Unreleased]\n* Item A\n\n## [Unreleased]* Item B\nB\n* Item C\n* Item D',
16+
'./CHANGELOG-INVALID_ORDER.md': '## [Unreleased]\n\n## [0.2.0]\n* Item A\n\n## [0.3.0]* Item B\nB\n\n## [0.1.0]\n* Item C\n* Item D',
1617
'./CHANGELOG-EMPTY.md': '## [Unreleased]\n\n\n\n## [1.0.0]\n\n* Item A\n* Item B',
1718
'./CHANGELOG-FULL.md':
1819
'# Changelog\n\n## [Unreleased]\n\n* Item A\n* Item B\n\n## [1.0.0] - 2020-05-02\n\n* Item C\n* Item D',
19-
'./CHANGELOG-NO-STRICT.md': '## [Unreleased]\n\n* Item A\n* Item B\n\n## [1.0.0] - 2020-05-02\n\n* Item C\n* Item D',
2020
'./CHANGELOG-DRYRUN.md': initialDryRunFileContents,
2121
'./CHANGELOG-LESS_NEW_LINES.md': '## [Unreleased]\n* Item A\n* Item B\n## [1.0.0] - 2020-05-02\n* Item C\n* Item D',
2222
'./CHANGELOG-EOL.md':
@@ -65,10 +65,15 @@ test('should throw for missing "unreleased" section when no-increment flag is se
6565
await assert.rejects(runTasks(plugin), /Missing "Unreleased" section in CHANGELOG-FOO.md/);
6666
});
6767

68-
test('should throw for missing "1.0.0" section when no-increment flag is set', async t => {
69-
const options = { increment: false, [namespace]: { filename: 'CHANGELOG-MISSING.md' } };
68+
test('should throw for multiple "unreleased" sections', async t => {
69+
const options = { [namespace]: { filename: 'CHANGELOG-MULTIPLE_UNRELEASED.md' } };
7070
const plugin = factory(Plugin, { namespace, options });
71-
await assert.rejects(runTasks(plugin), /Missing section for previous release \("1\.0\.0"\) in CHANGELOG-MISSING\.md/);
71+
await assert.rejects(runTasks(plugin), /Too many "Unreleased" sections in CHANGELOG-MULTIPLE_UNRELEASED.md: \d+./);
72+
});
73+
test('should throw for invalid order', async t => {
74+
const options = { [namespace]: { filename: 'CHANGELOG-INVALID_ORDER.md' } };
75+
const plugin = factory(Plugin, { namespace, options });
76+
await assert.rejects(runTasks(plugin), /Invalid sections order in CHANGELOG-INVALID_ORDER.md: 0.2.0, 0.3.0./);
7277
});
7378

7479
test('should find "1.0.0" section when no-increment flag is set when items under version', async t => {
@@ -85,12 +90,6 @@ test('should find "1.0.0" section when no-increment flag is set when items under
8590
assert.equal(plugin.getChangelog('1.0.0'), '* Item C\n* Item D');
8691
});
8792

88-
test('should throw for missing section for previous release', async t => {
89-
const options = { [namespace]: { filename: 'CHANGELOG-MISSING.md' } };
90-
const plugin = factory(Plugin, { namespace, options });
91-
await assert.rejects(runTasks(plugin), /Missing section for previous release \("1\.0\.0"\) in CHANGELOG-MISSING\.md/);
92-
});
93-
9493
test('should find very first changelog with disabled strict latest option', async t => {
9594
const options = { [namespace]: { filename: 'CHANGELOG-UNRELEASED.md', strictLatest: false } };
9695
const plugin = factory(Plugin, { namespace, options });
@@ -109,17 +108,6 @@ test('should write changelog', async t => {
109108
);
110109
});
111110

112-
test('should write changelog even with `strictLatest: false`', async t => {
113-
const options = { [namespace]: { filename: 'CHANGELOG-NO-STRICT.md', strictLatest: false } };
114-
const plugin = factory(Plugin, { namespace, options });
115-
await runTasks(plugin);
116-
assert.equal(plugin.getChangelog(), '* Item A\n* Item B');
117-
assert.match(
118-
readFile('./CHANGELOG-NO-STRICT.md'),
119-
/## \[1\.0\.1] - [0-9]{4}-[0-9]{2}-[0-9]{2}\n\n\* Item A\n\* Item B\n\n## \[1\.0\.0] - 2020-05-02\n\n\* Item C\n*\* Item D/
120-
);
121-
});
122-
123111
test('should not write changelog in dry run', async t => {
124112
const options = { 'dry-run': true, [namespace]: { filename: 'CHANGELOG-DRYRUN.md' } };
125113
const plugin = factory(Plugin, { namespace, options });

0 commit comments

Comments
 (0)