@alleyinteractive/changelog-extractor 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alleyinteractive/changelog-extractor",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "type": "module",
5
5
  "description": "Parse and extract structured information from Keep a Changelog formatted changelogs",
6
6
  "main": "src/parser.js",
@@ -15,9 +15,9 @@
15
15
  "prepublishOnly": "npm test",
16
16
  "prettier:fix": "npx prettier --write .",
17
17
  "prettier": "npx prettier --check .",
18
- "release:major": "npm run version:major && npm publish --access public && git push --follow-tags",
19
- "release:minor": "npm run version:minor && npm publish --access public && git push --follow-tags",
20
- "release:patch": "npm run version:patch && npm publish --access public && git push --follow-tags",
18
+ "release:major": "npm version major && npm publish --access public && git push --follow-tags",
19
+ "release:minor": "npm version minor && npm publish --access public && git push --follow-tags",
20
+ "release:patch": "npm version patch && npm publish --access public && git push --follow-tags",
21
21
  "test": "NODE_OPTIONS=--experimental-vm-modules NODE_NO_WARNINGS=1 npx jest"
22
22
  },
23
23
  "keywords": [
package/src/parser.js CHANGED
@@ -1,3 +1,83 @@
1
+ /**
2
+ * Unwrap text that has been wrapped across multiple lines.
3
+ * This fixes changelog text that has been hard-wrapped for readability
4
+ * but should flow as continuous text.
5
+ *
6
+ * @param {string} text - The text content to unwrap
7
+ * @returns {string} The unwrapped text
8
+ */
9
+ function unwrapText(text) {
10
+ const lines = text.split('\n')
11
+ const unwrappedLines = []
12
+
13
+ for (let i = 0; i < lines.length; i++) {
14
+ let currentLine = lines[i]
15
+
16
+ // Skip empty lines - preserve them as-is
17
+ if (currentLine.trim() === '') {
18
+ unwrappedLines.push(currentLine)
19
+ continue
20
+ }
21
+
22
+ // Collect continuation lines
23
+ let j = i + 1
24
+ while (j < lines.length) {
25
+ const nextLine = lines[j]
26
+
27
+ // Stop if next line is empty
28
+ if (nextLine.trim() === '') {
29
+ break
30
+ }
31
+
32
+ const trimmedCurrent = currentLine.trim()
33
+ const trimmedNext = nextLine.trim()
34
+
35
+ // Don't unwrap if current line looks like it ends a sentence/item
36
+ const endsWithPunctuation = /[.!?:;]$/.test(trimmedCurrent)
37
+ const endsWithCode = /`$/.test(trimmedCurrent)
38
+ const endsWithBullet = /[-*+]\s*$/.test(trimmedCurrent)
39
+
40
+ // Don't unwrap if next line starts a new bullet point, section, or sentence
41
+ const startsWithBullet = /^[-*+]\s/.test(trimmedNext)
42
+ const startsWithNumber = /^\d+\.\s/.test(trimmedNext)
43
+ const startsWithMarkdown = /^[#>]/.test(trimmedNext)
44
+
45
+ // More nuanced check for new sentences
46
+ const prevEndsWithContinuation =
47
+ /[,&]$/.test(trimmedCurrent) ||
48
+ /\b(and|or|but|with|for|to|from|in|on|at|by)$/.test(trimmedCurrent)
49
+ const startsWithCapitalNewSentence =
50
+ /^[A-Z]/.test(trimmedNext) &&
51
+ !prevEndsWithContinuation &&
52
+ endsWithPunctuation
53
+
54
+ // Determine if we should unwrap this line with the next
55
+ const shouldUnwrap =
56
+ !endsWithPunctuation &&
57
+ !endsWithCode &&
58
+ !endsWithBullet &&
59
+ !startsWithBullet &&
60
+ !startsWithNumber &&
61
+ !startsWithCapitalNewSentence &&
62
+ !startsWithMarkdown
63
+
64
+ if (shouldUnwrap) {
65
+ // Join the lines, preserving the original indentation of the first line
66
+ // but removing indentation from continuation lines
67
+ currentLine = currentLine + ' ' + trimmedNext
68
+ j++ // Move to next line
69
+ } else {
70
+ break // Stop unwrapping
71
+ }
72
+ }
73
+
74
+ unwrappedLines.push(currentLine)
75
+ i = j - 1 // Skip the lines we've consumed (j-1 because for loop will increment)
76
+ }
77
+
78
+ return unwrappedLines.join('\n')
79
+ }
80
+
1
81
  /**
2
82
  * Parse a Keep a Changelog formatted changelog file.
3
83
  *
@@ -98,7 +178,9 @@ export function parseChangelog(changelogContent, version = null) {
98
178
 
99
179
  const content = currentSectionContent.join('\n')
100
180
  if (content.trim()) {
101
- currentVersion.sections[currentSection] = content
181
+ // Unwrap any text that has been hard-wrapped across lines
182
+ const unwrappedContent = unwrapText(content)
183
+ currentVersion.sections[currentSection] = unwrappedContent
102
184
  }
103
185
  }
104
186
  }
@@ -120,7 +202,9 @@ export function parseChangelog(changelogContent, version = null) {
120
202
 
121
203
  const content = trimmedContent.join('\n')
122
204
  if (content.trim()) {
123
- currentVersion.sections['general'] = content
205
+ // Unwrap any text that has been hard-wrapped across lines
206
+ const unwrappedContent = unwrapText(content)
207
+ currentVersion.sections['general'] = unwrappedContent
124
208
  }
125
209
  generalContent = []
126
210
  }