changelog-tool 0.7.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -18,7 +18,7 @@ jobs:
18
18
 
19
19
  strategy:
20
20
  matrix:
21
- node-version: [16.x, 18.x]
21
+ node-version: [16.x, 18.x, 20.x]
22
22
  # See supported Node.js release schedule at https://nodejs.org/en/about/releases/
23
23
 
24
24
  steps:
package/changelog.md CHANGED
@@ -1,6 +1,32 @@
1
1
  Changelog
2
2
  =========
3
3
 
4
+ 1.0.0 (2023-06-20)
5
+ ------------------
6
+
7
+ First stable release! Just kidding, it was already stable.
8
+
9
+ * Add support for [Markdown reference links][1]. References are a Markdown
10
+ feature that lets you write links in paragraphs, but put the actual target
11
+ near the end of the document similar to references in technical documents.
12
+ This can declutter the reading experience for those reading the Markdown
13
+ sources. The tool doesn't let you quickly add links via the CLI yet, but it
14
+ will no longer mangle them when they appear.
15
+ * Testing Node 20
16
+ * Bugfix: Always insert an empty line between the 'preface' and bulletpoints
17
+ sections of a version block.
18
+
19
+
20
+ 0.7.2 (2023-02-17)
21
+ ------------------
22
+
23
+ * Added a `--nowrap` option to `show`, which doesn't wrap long lines. This is
24
+ useful for copy-pasting changelog into places where linebreaks are
25
+ significant, such as the Github releases section.
26
+ * Support multiple digits for the alpha/beta release string.
27
+ * Also allows setting the changelog message as positional arguments.
28
+
29
+
4
30
  0.7.1 (2023-02-14)
5
31
  ------------------
6
32
 
@@ -60,4 +86,7 @@ Changelog
60
86
  0.1.0 (2023-02-08)
61
87
  ------------------
62
88
 
63
- * Implemented the 'help' and 'init' commands.
89
+ * Implemented the 'help' and 'init' commands
90
+
91
+ [1]: https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet#link "Markdown cheatsheet: Links"
92
+
package/changelog.mjs CHANGED
@@ -10,13 +10,21 @@ export class Changelog {
10
10
  */
11
11
  versions = [];
12
12
 
13
+ /**
14
+ * @type {Link[]}
15
+ */
16
+ links = [];
17
+
13
18
  toString() {
14
19
 
15
20
  return (
16
21
  this.title + '\n' +
17
22
  ('='.repeat(this.title.length)) + '\n' +
18
23
  '\n' +
19
- this.versions.map(version => version.toString()).join('\n\n')
24
+ this.versions.map(version => version.toString()).join('\n\n') +
25
+
26
+ // Sorry about this line future me (or someone else)
27
+ (this.links.length > 0 ? ('\n' + this.links.map( link => wrap(`[${link.name}]: ${link.href}${link.title?` "${link.title}"`:''}`, link.name.length+4)).join('\n') + '\n') : '')
20
28
  );
21
29
 
22
30
  }
@@ -116,15 +124,28 @@ export class VersionLog {
116
124
 
117
125
  toString() {
118
126
 
127
+ return this.output();
128
+
129
+ }
130
+
131
+ /**
132
+ * Renders the changelog as a string.
133
+ *
134
+ * @param {boolean} lineWrap
135
+ * @returns {string}
136
+ */
137
+ output(lineWrap = true) {
138
+
139
+ const lineLength = lineWrap ? 79 : Infinity;
119
140
  const title = this.version + ' (' + (this.date ?? '????-??-??') + ')';
120
141
  return (
121
142
  title + '\n' +
122
143
  ('-'.repeat(title.length)) + '\n' +
123
- (this.preface ? '\n' + wrap(this.preface) : '') +
144
+ (this.preface ? '\n' + wrap(this.preface, 0, lineLength) + '\n' : '') +
124
145
  '\n' +
125
- this.items.map(version => version.toString()).join('\n') +
146
+ this.items.map(version => version.output(lineWrap)).join('\n') +
126
147
  '\n' +
127
- (this.postface ? '\n' + wrap(this.postface) + '\n' : '')
148
+ (this.postface ? '\n' + wrap(this.postface, 0, lineLength) + '\n' : '')
128
149
  );
129
150
 
130
151
  }
@@ -134,7 +155,7 @@ export class VersionLog {
134
155
  export class LogItem {
135
156
 
136
157
  /**
137
- * @type string
158
+ * @type {string}
138
159
  */
139
160
  message;
140
161
 
@@ -145,6 +166,17 @@ export class LogItem {
145
166
  this.message = message;
146
167
  }
147
168
 
169
+ /**
170
+ * Renders the changelog as a string.
171
+ *
172
+ * @param {boolean} lineWrap
173
+ * @returns {string}
174
+ */
175
+ output(lineWrap = true) {
176
+ const lineLength = lineWrap ? 79 : Infinity;
177
+ return wrap('* ' + this.message, 2, lineLength);
178
+ }
179
+
148
180
  toString() {
149
181
 
150
182
  return wrap('* ' + this.message, 2);
@@ -153,4 +185,9 @@ export class LogItem {
153
185
 
154
186
  }
155
187
 
156
-
188
+ /**
189
+ * @typedef Link {Object}
190
+ * @property Link.href {string}
191
+ * @property Link.name {string}
192
+ * @property Link.title {string|null}
193
+ */
package/cli.mjs CHANGED
@@ -49,6 +49,10 @@ async function main() {
49
49
  type: 'boolean',
50
50
  description: 'Indicates that the current change is a major change.',
51
51
  },
52
+ nowrap: {
53
+ type: 'boolean',
54
+ description: 'Don\'t wrap "show" output'
55
+ }
52
56
  },
53
57
  allowPositionals: true,
54
58
  });
@@ -79,7 +83,12 @@ async function main() {
79
83
  changeType = 'major';
80
84
  }
81
85
  if (!values.message) {
82
- throw new Error('The "-m" or "--message" argument is required');
86
+ if (positionals.length>1) {
87
+ // We also support setting a message after the command instead of -m
88
+ values.message = positionals.slice(1).join(' ');
89
+ } else {
90
+ throw new Error('The "-m" or "--message" argument is required');
91
+ }
83
92
  }
84
93
  await add({
85
94
  message: values.message,
@@ -93,7 +102,11 @@ async function main() {
93
102
  await format();
94
103
  break;
95
104
  case 'show' :
96
- await show({ all: !!values.all, version: positionals[1]});
105
+ await show({
106
+ all: !!values.all,
107
+ version: positionals[1],
108
+ noWrap: !!values.nowrap
109
+ });
97
110
  break;
98
111
  case 'list' :
99
112
  await list();
@@ -170,8 +183,9 @@ async function list() {
170
183
  * @param {Object} showOptions
171
184
  * @param {boolean} showOptions.all Show all versions
172
185
  * @param {string?} showOptions.version show a specific version
186
+ * @param {boolean?} showOptions.noWrap don't line-wrap output
173
187
  */
174
- async function show({all, version}) {
188
+ async function show({all, version, noWrap}) {
175
189
 
176
190
  const changelog = await parseChangelog();
177
191
 
@@ -186,7 +200,7 @@ async function show({all, version}) {
186
200
 
187
201
  console.log(
188
202
  toRender
189
- .map( log => log.toString())
203
+ .map( log => log.output(!noWrap))
190
204
  .join('\n\n')
191
205
  );
192
206
 
package/package.json CHANGED
@@ -1,13 +1,20 @@
1
1
  {
2
2
  "name": "changelog-tool",
3
- "version": "0.7.1",
3
+ "version": "1.0.0",
4
4
  "description": "A CLI tool for manipulating changelogs",
5
5
  "type": "module",
6
6
  "main": "index.mjs",
7
+ "homepage": "https://github.com/evert/changelog-tool",
8
+ "author": "Evert Pot (https://evertpot.com/)",
9
+ "license": "MIT",
7
10
  "scripts": {
8
11
  "test": "node --test",
9
12
  "watch": "tsc --watch"
10
13
  },
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git@github.com:evert/changelog-tool.git"
17
+ },
11
18
  "keywords": [
12
19
  "changelog",
13
20
  "markdown"
package/parse.mjs CHANGED
@@ -14,6 +14,10 @@ export async function parseFile(filename) {
14
14
 
15
15
  }
16
16
 
17
+ const linkReferenceRe = /^\[([a-zA-Z0-9])+\]:/;
18
+ const versionRe = /^([0-9\.]{3,}(?:-(?:alpha|beta)\.[0-9]+)?) \(([0-9]{4}-[0-9]{2}-[0-9]{2}|\?\?\?\?-\?\?-\?\?)\)$/;
19
+
20
+
17
21
  /**
18
22
  * @param {string} changelogInput
19
23
  * @returns {Changelog}
@@ -51,10 +55,31 @@ export function parse(changelogInput) {
51
55
  continue;
52
56
  }
53
57
 
58
+ // Look for link references
59
+ if (linkReferenceRe.test(line)) {
60
+
61
+ let linkRefLine = line;
62
+ while(lines[idx+1]?.match(/^\W\W/)) {
63
+ // If the line was folded over multiple lines, read those too.
64
+ linkRefLine += ' ' + lines[idx+1].trim();
65
+ idx++;
66
+ }
67
+ const name = linkRefLine.match(linkReferenceRe)?.[1];
68
+ const href = linkRefLine.split(' ')[1];
69
+ const title = linkRefLine.includes('"') ? linkRefLine.substring(linkRefLine.indexOf('"')+1,linkRefLine.lastIndexOf('"')) : null;
70
+ changelog.links.push({
71
+ name: /** @type {string} */(name),
72
+ href,
73
+ title
74
+ });
75
+ continue;
76
+
77
+ }
78
+
54
79
  // Look to the next line for ----
55
80
  if (lines[idx+1]?.match(/^-{1,}$/)) {
56
81
  // Found a new Version
57
- const matches = line.match(/^([0-9\.]{3,}(?:-(?:alpha|beta)\.[0-9])?) \(([0-9]{4}-[0-9]{2}-[0-9]{2}|\?\?\?\?-\?\?-\?\?)\)$/);
82
+ const matches = line.match(versionRe);
58
83
 
59
84
  if (!matches) {
60
85
  throw new Error(`A version title must have the format "1.0.0 (YYYY-MM-DD)" or "1.0.0 (????-??-??)" for unreleased versions. We found: "${line}"`);
package/readme.md CHANGED
@@ -8,6 +8,9 @@ This tool currently expects to work with a file named 'changelog.md' in the
8
8
  current working directory. This is a markdown file that looks like this:
9
9
 
10
10
  ```
11
+ Changelog
12
+ =========
13
+
11
14
  0.4.0 (????-??-??)
12
15
  ------------------
13
16
 
package/test/parse.mjs CHANGED
@@ -83,7 +83,6 @@ Well, that's all folks.
83
83
  *
84
84
  `;
85
85
 
86
- debugger;
87
86
  const result = await parse(input);
88
87
 
89
88
  const latest = result.get('0.2.0');
@@ -93,3 +92,44 @@ Well, that's all folks.
93
92
 
94
93
 
95
94
  });
95
+
96
+ test('Link references', async() => {
97
+
98
+
99
+ const input = `Changesss
100
+ =========
101
+
102
+ 0.2.0 (????-??-??)
103
+ ------------------
104
+
105
+ WOW another release. How good is that?
106
+ Here's another line.
107
+
108
+ * Implemented the 'list' command.
109
+ * Added testing framework. See [the blog post][1] for more information.
110
+
111
+ 0.1.0 (2023-02-08)
112
+ ------------------
113
+
114
+ * Implemented the ['help'][2] and 'init' commands.
115
+
116
+ [1]: https://evertpot.com/ "My Blog"
117
+ [2]: https://indieweb.social/@evert "My Mastodon account, but it's split
118
+ over two lines"
119
+ `;
120
+
121
+ debugger;
122
+ const result = await parse(input);
123
+
124
+ assert.deepEqual({
125
+ name: '1',
126
+ href: 'https://evertpot.com/',
127
+ title: 'My Blog',
128
+ }, result.links[0]);
129
+ assert.deepEqual({
130
+ name: '2',
131
+ href: 'https://indieweb.social/@evert',
132
+ title: 'My Mastodon account, but it\'s split over two lines',
133
+ }, result.links[1]);
134
+
135
+ });